/**
 * YWA — interior page sections
 * ---------------------------------------------------------------------------
 * The section blocks the five interior pages are composed from. Sibling sheet
 * to home.css, which owns the homepage's five sections; kept separate so the
 * homepage's layout rules and the interior pages' don't accumulate in one file
 * that neither page fully uses.
 *
 * Loaded per-block, not per-page: each block.json names `ywa-sections` in its
 * `style` field, so WordPress loads this only where one of these blocks appears.
 *
 * Token-bound. MEASURED marks a value taken from the Figma design rather than
 * from the space scale.
 *
 * GEOMETRY, measured from the designs (fileKey r5LqBJj5Kzz3Feq1p1ivTh, page
 * WordPress) rather than eyeballed:
 *   content column      1280   — same as the homepage, so edges align site-wide
 *   two-column intro     600 | 80 | 600
 *   text-only intro      768   — a reading measure, not the full column
 *   section padding      112 in Figma → the .section rhythm (64 / 96 at lg) here,
 *                        because 112 is off the space scale. Divergence logged
 *                        on the reconciliation checklist.
 */

/* ===========================================================================
   PAGE INTRO — the opener every interior page starts with
   ---------------------------------------------------------------------------
   One block covers all five pages. About and Donation pair the text with a
   photo; History and Contact are text only. The text column is the SAME width
   either way (a reading measure), so the pages share an optical left edge and a
   text-only page doesn't stretch its prose across the full 1280.
   =========================================================================== */

.page-intro {
  padding-block: var(--space-1600); /* 64 — Figma 112, off-scale; see header */
}

@media (min-width: 1024px) {
  .page-intro { padding-block: var(--space-2400); } /* 96 */
}

.page-intro__inner {
  display: grid;
  gap: var(--space-2000); /* MEASURED — 80 between the columns */
  align-items: stretch;   /* the photo fills the row's height, as in the design */
}

/* With a photo: two equal columns. Without: one column at the reading measure.
   `--page-intro-measure` is the text column's cap in BOTH cases, so the prose
   sets to the same width whether or not a photo is present. */
.page-intro {
  --page-intro-measure: 768px; /* MEASURED — text-only intro width */
}

.page-intro__text {
  max-width: var(--page-intro-measure);
  display: flex;
  flex-direction: column;
  gap: var(--space-400); /* MEASURED — 16 heading→body */
}

.page-intro__title {
  margin: 0;
  color: var(--color-text-strong);
}

.page-intro__body > * { margin: 0; }
.page-intro__body > * + * { margin-block-start: var(--space-600); } /* 24 between paragraphs */
.page-intro__body { color: var(--color-text-default); }

/* "Other ways to give" on the Donation page — a bulleted list under the prose.
   NO `margin: 0` here. base.css already zeroes every <ul>, so it bought nothing
   — but it sat later in this file than `.page-intro__body > * + *` at equal
   specificity (0,1,0), so it cancelled the 24px stack gap and welded the list
   to the label above it. Fixed 1.6.3; measured at 0px before, 24px after. */
.page-intro__list {
  padding-inline-start: var(--space-600);
}

.page-intro__list > li + li { margin-block-start: var(--space-300); } /* 12 */

.page-intro__media { min-width: 0; }

.page-intro__photo {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 20rem;
  object-fit: cover;
}

/* Placeholder standing in for a photo the client has not supplied yet. Uses the
   same .ph-img treatment as the homepage so an unfilled slot reads the same way
   everywhere, and disappears with it once the real photos land. */
.page-intro__photo.ph-img {
  min-height: 22rem;
  border-radius: 0;
  /* Restated because .page-intro__photo above sets `display: block`, which is
     correct for a real <img> but would strand the placeholder's label in the
     top-left instead of centring it. */
  display: grid;
  place-items: center;
}

@media (min-width: 768px) {
  .page-intro--has-media .page-intro__inner {
    grid-template-columns: 1fr 1fr; /* MEASURED — 600 | 600 inside the 1280 column */
  }
  /* Paired with a photo the text no longer needs its own cap — the column
     already holds it to ~600, narrower than the measure. */
  .page-intro--has-media .page-intro__text { max-width: none; }
}

/* ===========================================================================
   POST GRID — News & Events' "News & Announcements"
   ---------------------------------------------------------------------------
   MEASURED: three columns of 405 at a 32 gap in the 1280 column, "View all" 64
   below the row.

   The card is the shared .event-card component (components.css). Only the
   arrangement is here — the homepage scrolls the same cards horizontally, this
   page lays them out flat.
   =========================================================================== */

.post-grid { padding-block: var(--space-1600); }

@media (min-width: 1024px) {
  .post-grid { padding-block: var(--space-2400); }
}

.post-grid__title { margin: 0 0 var(--space-2000); } /* MEASURED — 80 */

.post-grid__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: var(--space-800); /* MEASURED — 32 */
}

@media (min-width: 1024px) {
  .post-grid__list { grid-template-columns: repeat(3, 1fr); } /* MEASURED — 3 up */
}

/* The card sets its own width for the carousel; in a grid the track decides.
   Without this the cards keep the carousel's fixed 24rem and overflow their
   columns. */
.post-grid__list .event-card { width: auto; }

.post-grid__actions {
  margin: var(--space-1600) 0 0; /* MEASURED — 64 below the row */
  display: flex;
  justify-content: center;
}

/* ===========================================================================
   SINGLE EVENT — the detail page
   ---------------------------------------------------------------------------
   NOT DESIGNED. There is no event detail page in the Figma file; this is
   assembled from the page-intro band and existing type roles so it looks like
   it belongs, not like it was drawn. Wants a proper design pass before launch.
   See single-ywa_event.php.
   =========================================================================== */

/* The date leads, above the title — on a page about something that happens at a
   time, "when" is the question the visitor arrived with. */
.ywa-event__when {
  margin: 0 0 var(--space-300);
  color: var(--color-text-subdued);
}

.ywa-event__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-400);
  margin: 0 0 var(--space-600);
  font-size: var(--type-size-body-small);
  color: var(--color-text-subdued);
}

/* Matches the treatment on the event-list rows, so "sold out" reads the same
   wherever it appears rather than being a different idea per page. */
.ywa-event__soldout { color: var(--color-text-subdued); }

.ywa-event__body > * + * { margin-block-start: var(--space-400); }
.ywa-event__action { margin-block-start: var(--space-800); }

/* A way back. This post type has no archive of its own, so without it someone
   who followed a forwarded link has nowhere obvious to go next. */
.ywa-event__return { padding-block: 0 var(--space-2400); }

/* ===========================================================================
   EVENT LIST — News & Events' upcoming events
   ---------------------------------------------------------------------------
   MEASURED: the featured event is 616 | 48 | 616 in the 1280 column, 411 tall;
   the compact rows sit 64 below it, full width, stacked with hairlines and no
   gap between them.
   =========================================================================== */

.event-list { padding-block: var(--space-1600); }

@media (min-width: 1024px) {
  .event-list { padding-block: var(--space-2400); }
}

.event-list__title { margin: 0 0 var(--space-2000); }

.event-list__eyebrow {
  margin: 0;
  font-size: var(--type-size-body-small);
  letter-spacing: var(--type-tracking-eyebrow);
  text-transform: uppercase;
  color: var(--color-text-subdued);
}

.event-list__place {
  margin: 0;
  font-size: var(--type-size-body-small);
  color: var(--color-text-subdued);
}

/* ---- the featured event ---- */

.event-feature { display: grid; gap: var(--space-800); }

.event-feature__media { position: relative; min-width: 0; }

.event-feature__img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 616 / 411; /* MEASURED */
  object-fit: cover;
  border-radius: var(--radius-md);
}

.event-feature__body {
  display: grid;
  align-content: center;
  gap: var(--space-600); /* MEASURED — 24 */
  min-width: 0;
}

.event-feature__title { margin: 0; }
.event-feature__desc { margin: 0; color: var(--color-text-subdued); }
.event-feature__action { margin: 0; }

@media (min-width: 1024px) {
  .event-feature {
    grid-template-columns: 1fr 1fr; /* MEASURED — 616 | 616 */
    gap: var(--space-1200);         /* MEASURED — 48 */
    align-items: stretch;
  }
  .event-feature__img { aspect-ratio: auto; height: 411px; } /* MEASURED */
}

/* ---- the date chip ---- */

.event-chip {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 2px;
  inline-size: 72px;
  padding-block: var(--space-300);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sm);
  background-color: var(--color-surface-raised);
  text-align: center;
}

/* On the featured card the chip sits over the photograph, so it needs to be
   readable against whatever that photograph happens to be — hence the solid
   surface behind it rather than a transparent overlay. */
.event-feature__media .event-chip {
  position: absolute;
  inset-block-start: var(--space-400);
  inset-inline-start: var(--space-400);
}

.event-chip__weekday,
.event-chip__month {
  font-size: var(--type-size-body-small);
  line-height: 1.2;
  color: var(--color-text-subdued);
}

.event-chip__day {
  font-size: var(--type-size-heading-4);
  font-weight: var(--type-weight-medium);
  line-height: 1.1;
  color: var(--color-text-strong);
}

/* ---- the compact rows ---- */

.event-rows {
  list-style: none;
  margin: var(--space-1600) 0 0; /* MEASURED — 64 below the featured card */
  padding: 0;
  border-block-start: 1px solid var(--color-border-subtle);
}

/* Rows butt together with no gap, separated by a hairline — so the base
   `li + li` prose margin has to go, or every row after the first drifts. */
.event-rows > li { margin-block-start: 0; }

.event-row {
  display: grid;
  gap: var(--space-400);
  align-items: center;
  padding-block: var(--space-600);
  border-block-end: 1px solid var(--color-border-subtle);
}

.event-row__body { display: grid; gap: var(--space-200); min-width: 0; }
.event-row__title { margin: 0; }
.event-row__desc { margin: 0; color: var(--color-text-subdued); }
.event-row__action { margin: 0; }

/* An undated event still occupies the chip's column, so its title stays in line
   with every other title rather than sliding left on its own. */
.event-row__nodate { display: block; inline-size: 72px; }

.event-row__soldout {
  margin-inline-start: var(--space-300);
  font-size: var(--type-size-body-small);
  font-weight: var(--type-weight-regular);
  color: var(--color-text-subdued);
}

@media (min-width: 768px) {
  .event-row {
    grid-template-columns: 72px 1fr auto;
    gap: var(--space-800);
  }
}

/* ===========================================================================
   BOARD GRID — About's Board of Trustees
   ---------------------------------------------------------------------------
   MEASURED: four columns of 296 at a 32 gap, rows 64 apart, in the 1280 column.
   Expressed as auto-fit rather than a fixed 4 so the grid reflows on its own at
   every width — 17 people is not a number that divides into tidy rows, and the
   last row being short is the design's own outcome, not a bug to correct.
   =========================================================================== */

/* The band's own padding. Every other section on the site has this; the board
   grid was written without it, so on About the trustees butted straight against
   the intro above and the footer below with no air at all. Same values as
   .page-intro and the rest (Figma's 112 rounds to 96 on the scale). */
.board-grid { padding-block: var(--space-1600); }

@media (min-width: 1024px) {
  .board-grid { padding-block: var(--space-2400); }
}

.board-grid__head {
  max-width: 768px; /* MEASURED — prose keeps the reading measure; the cards do not */
  margin-block-end: var(--space-2000);
}

.board-grid__title { margin: 0 0 var(--space-600); }
.board-grid__intro { margin: 0; color: var(--color-text-default); }
.board-grid__intro + .board-grid__intro { margin-block-start: var(--space-400); }

/* base.css gives every `li + li` an 8px top margin for prose lists. In a grid
   the spacing is owned by `gap`, and that stray margin pushes every item except
   the first down by 8 — so the first card in each row sat 8px above its
   neighbours. Invisible in a screenshot, obvious the moment you measure a
   photo's top edge. Reset wherever list items are laid out as grid cells.
   (Left alone in the article's chapter list, which is a normal flow list where
   the prose rhythm is the right behaviour.) */
.board-grid__list > li,
.timeline__track > li {
  margin-block-start: 0;
}

.board-grid__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  column-gap: var(--space-800);  /* MEASURED — 32 */
  row-gap: var(--space-1600);    /* MEASURED — 64 */
}

@media (min-width: 1024px) {
  .board-grid__list { grid-template-columns: repeat(4, 1fr); } /* MEASURED — 4 up */
}

/* Grid rather than flex, deliberately. As a flex column the card picks up
   height from the whitespace between its child elements, so a card whose markup
   is formatted differently comes out a few pixels taller and its photo sits off
   the row's top edge — measured at 8px between two cards that differed only in
   line breaks. Every card the block renders is formatted identically, so this
   would not bite today; it would bite the first time anything generates one
   differently. Grid places only element children, so the card's height depends
   on its content and nothing else. */
.board-card {
  display: grid;
  align-content: start;
  gap: var(--space-400);
}

.board-card__photo {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1; /* MEASURED — square */
  object-fit: cover;
  border-radius: var(--radius-md);
}

.board-card__text { display: flex; flex-direction: column; gap: var(--space-100); }

.board-card__name {
  margin: 0;
  font-size: var(--type-size-body);
  font-weight: var(--type-weight-medium);
  color: var(--color-text-strong);
}

/* A card with no name yet. Reads as an unfilled slot rather than as a person
   whose name is missing. */
.board-card__name--empty { color: var(--color-text-subdued); }

.board-card__role {
  margin: 0;
  font-size: var(--type-size-body);
  color: var(--color-text-default);
}

.board-card__year {
  margin-block-start: var(--space-300);
  margin-block-end: 0;
  font-size: var(--type-size-body-small);
  color: var(--color-text-subdued);
}

/* ===========================================================================
   TIMELINE — History's decade run
   ---------------------------------------------------------------------------
   MEASURED: 7 columns of 176 at an 8 gap (7×176 + 6×8 = 1280 exactly), each
   column 180 / 15 / 180 with 16 between = 407 tall.

   The block paints no background and claims no tone — it continues the band of
   the section above it. See timeline/render.php for why that matters to the
   alternation. The rule below reaches BACKWARD to trim that section's bottom
   padding, so the gap between the intro's last paragraph and the timeline is the
   design's 80 rather than the 112 a stacked band would give. `:has()` is the
   only way to express "the section that precedes a timeline" in CSS.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   ATTACHED SECTIONS — two blocks that are really one band
   ---------------------------------------------------------------------------
   Several designs put a page opener and the content under it inside a SINGLE
   frame: History's intro + timeline, News & Events' intro + event list. One
   band, one set of padding, and a smaller gap between the two parts than the
   band's own padding.

   Built as two blocks, that comes out wrong — each contributes its own padding,
   so the gap doubles to ~190px and the page looks like it has a hole in it.

   An attached section drops its top padding and reaches BACK to trim the
   padding of whatever precedes it. `:has()` is the only way to say "the section
   before an attached one" in CSS, since a selector cannot otherwise look
   forward. Matched on the ELEMENT, not a `.section` class — the YWA blocks each
   carry their own class (.page-intro, .board-grid) and share only the <section>
   tag. An earlier pass used `.section:has(…)`, which silently matched nothing;
   caught by measuring the gap rather than looking at it.
   --------------------------------------------------------------------------- */

.is-attached { padding-block-start: 0; }

section:has(+ .is-attached) { padding-block-end: var(--space-1200); } /* 48 — narrow */

@media (min-width: 1024px) {
  section:has(+ .is-attached) { padding-block-end: var(--space-2000); } /* MEASURED — 80 */
}

/* The timeline is ALWAYS attached — it has no heading of its own, so it can
   only ever be the continuation of something. Its bottom is a normal band
   bottom (Figma's 112 rounds to 96, the call already made for every band). */
.timeline { padding-block-end: var(--space-1600); }

@media (min-width: 1024px) {
  .timeline { padding-block-end: var(--space-2400); }
}

.timeline__track {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-1600); /* vertical stack on narrow screens */
}

.timeline__item { display: grid; }
.timeline__media { min-width: 0; }

.timeline__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--radius-md);
}

.timeline__label {
  margin: 0;
  font-size: var(--type-size-body);
  font-weight: var(--type-weight-medium);
  color: var(--color-text-strong);
}

.timeline__caption {
  margin: var(--space-100) 0 0;
  font-size: var(--type-size-body-small);
  color: var(--color-text-subdued);
}

/* ---- narrow: a vertical run, line down the left ---- */

@media (max-width: 767px) {
  .timeline__item {
    grid-template-columns: 72px var(--space-500) 1fr;
    align-items: start;
    column-gap: var(--space-400);
  }
  .timeline__media { grid-column: 1; }
  .timeline__photo { aspect-ratio: 1 / 1; }
  .timeline__axis  { grid-column: 2; position: relative; align-self: stretch; }
  .timeline__text  { grid-column: 3; }

  /* The line runs between this milestone's dot and the next one's, so it stops
     at the last dot instead of trailing into empty space. */
  .timeline__item:not(:last-child) .timeline__axis::after {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 50%;
    translate: -50% 0;
    border-inline-start: 2px solid currentColor;
  }
  .timeline__dot { margin-block-start: var(--space-200); }
}

/* ---- wide: the horizontal run from the design ---- */

@media (min-width: 768px) {
  .timeline__track {
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: var(--space-200); /* MEASURED — 8 */
  }

  .timeline__item {
    grid-template-rows: 180px 15px 180px; /* MEASURED */
    row-gap: var(--space-400);            /* MEASURED — 16 */
  }

  /* Odd milestones: picture above the line, words below. */
  .timeline__media { grid-row: 1; align-self: end; }
  .timeline__axis  { grid-row: 2; position: relative; }
  .timeline__text  { grid-row: 3; align-self: start; }

  /* Even milestones: the mirror image. Decided here rather than in PHP so that
     adding or removing a milestone re-alternates the whole run for free. */
  /* Only the ROW swaps. Text stays left-aligned in every column — in the design
     each label sits at the left edge of its own column whichever side of the
     line it is on, so the run reads as one rhythm rather than alternating
     alignment as well as position. */
  .timeline__item:nth-child(even) .timeline__media { grid-row: 3; align-self: start; }
  .timeline__item:nth-child(even) .timeline__text  { grid-row: 1; align-self: end; }

  .timeline__photo { height: 180px; } /* MEASURED */

  /* The connecting line overruns into the 8px gap so the run reads as one
     continuous rule rather than seven separate dashes. Not on the last item —
     there it would push past the container and cost a horizontal scrollbar. */
  .timeline__item:not(:last-child) .timeline__axis::after {
    content: "";
    position: absolute;
    inset-block-start: 50%;
    translate: 0 -50%;
    inset-inline-start: 0;
    width: calc(100% + var(--space-200));
    border-block-start: 2px solid currentColor;
  }
}

.timeline__axis { display: flex; align-items: center; }

/* TOKEN NOTE — the axis is drawn in `currentColor`, deliberately, and that is a
   deferral rather than a choice.

   The system has no role for diagram ink. `border.strong` looked like the
   answer for the line, but it resolves to a mid grey (#9E9E9E) because the
   border scale is built to be SUBORDINATE — dividers should recede. A timeline
   axis is the opposite: it is the section's primary graphic, and the design
   draws it in ink. Wrong value is a symptom here, not the disease; the real
   finding is that "structural illustration" is a role the system cannot name,
   which is also why the dot had no honest token.

   `currentColor` is what icons do, and it is right for the same reason: the
   diagram is drawn in the ink of the text it belongs to, so it stays correct in
   dark mode and needs no token at all. A `graphic.*` role is proposed on the
   reconciliation checklist for the case where a diagram must diverge from its
   surrounding text. */
.timeline__dot {
  position: relative;
  z-index: 1; /* over the connecting line */
  display: block;
  inline-size: 15px;  /* MEASURED */
  block-size: 15px;
  border-radius: var(--radius-full);
  background-color: currentColor;
}

/* ===========================================================================
   ARTICLE BAND — long-form prose with a sidebar (Whiffenpoof History)
   ---------------------------------------------------------------------------
   MEASURED: prose 768 · gutter 192 · sidebar 320, inside the 1280 column. The
   very wide gutter is the design's, and it earns its keep — it is what stops a
   768px measure and a 320px column reading as a two-column newspaper.

   The prose is INNER BLOCKS, so everything in here has to survive whatever
   Frank puts in it: paragraphs, headings, images with captions, quotes, lists.
   That is why the rules below reach for `.flow` and generic child selectors
   rather than styling a known set of elements.
   =========================================================================== */

.article-band {
  padding-block: var(--space-1600);
}

@media (min-width: 1024px) {
  .article-band { padding-block: var(--space-2400); }
}

.article-band__inner { display: grid; }

.article-band__prose { min-width: 0; max-width: 768px; } /* MEASURED */

/* Images an editor drops into the article. Core emits <figure><img><figcaption>,
   so the caption is styled here rather than requiring a custom block. Full
   width of the measure by default — a photo narrower than the text column reads
   as a mistake in long-form. */
/* A picture needs more air around it than one paragraph needs from the next, or
   it reads as glued to the sentence above. `.flow` gives siblings 16; a figure
   in long-form wants twice that on both sides. */
.article-band__prose figure { margin: 0; }
.article-band__prose > figure { margin-block: var(--space-800); }
.flow.article-band__prose > * + figure { margin-block-start: var(--space-800); }
.article-band__prose img { display: block; width: 100%; height: auto; border-radius: var(--radius-md); }

/* The caption's type is restated here rather than applied as a role class,
   because core emits the <figcaption> and there is nowhere to put one. It
   mirrors `.type-body-small` exactly — small body copy, which is what a caption
   is; `meta` would be wrong, that role is for dates and labels.
   (An earlier pass reached for `--type-size-small`, which does not exist. The
   declaration was silently dropped and captions rendered at full body size —
   caught by reading the computed value, not by looking at it.) */
/* Quotations. base.css gives every blockquote the accent rule, the subdued
   colour and the LEAD size — a pull-quote treatment, which is right for a short
   line lifted out of an ordinary post for emphasis.

   It is wrong for this. The history is a scholarly article quoting source
   material at length: twenty quotations, several over 150 words, together about
   a quarter of the page. Set a step above body, the quoted sources visually
   outrank the authors' own prose — the hierarchy inverts. Extended quotation is
   conventionally set at body size or smaller and marked as quoted by the indent
   and rule, which base.css already supplies.

   Scoped here rather than changed in base.css: an ordinary post with one short
   quote still wants the pull-quote treatment. (Josh, 2026-07-27.)

   They also want the same air a figure gets, rather than the 16 `.flow` gives
   ordinary siblings. */
.article-band__prose blockquote { font-size: var(--type-size-body); }
.article-band__prose > blockquote { margin-block: var(--space-800); }
.flow.article-band__prose > * + blockquote { margin-block-start: var(--space-800); }

/* The attribution. Core emits <cite>, which browsers render inline and italic —
   so it ran on from the end of the quotation as though it were part of it. On
   its own line, at the caption size, it reads as a source. */
.article-band__prose blockquote cite {
  display: block;
  margin-block-start: var(--space-300);
  font-size: var(--type-size-body-small);
  font-style: normal;
}

.article-band__prose figcaption {
  margin-block-start: var(--space-300);
  color: var(--color-text-subdued);
  font-size: var(--type-size-body-small);
  line-height: var(--type-leading-body);
}

/* A slot where an image used to be. The source article's image links are dead,
   leaving their captions stranded in the prose — as plain paragraphs those read
   as stray sentences ("The Hogans of 1909" on its own line). Dashed, subdued and
   labelled, they read as scaffolding instead, and they hold the caption until
   someone adds the real scan.

   Deliberately visible to VISITORS, not just editors, unlike the hero's editor
   note: this is staging, and a missing photograph in a historical article is
   something Frank should be able to see and chase. Delete the paragraph once
   the image is in. */
.ywa-missing-figure {
  margin-block: var(--space-800);
  padding: var(--space-400) var(--space-500);
  border: 1px dashed currentColor;
  border-radius: var(--radius-sm);
  color: var(--color-text-subdued);
  font-size: var(--type-size-body-small);
}

/* ---- the sidebar ---- */

.article-band__aside { min-width: 0; }

.article-band__note-title { margin: 0 0 var(--space-300); color: var(--color-text-strong); }
.article-band__note-body { margin: 0; color: var(--color-text-default); }

.article-band__toc { margin-block-start: var(--space-800); }
.article-band__toc ul { list-style: none; margin: 0; padding: 0; }

/* Chapter headings are anchor targets. Without the scroll margin a jump lands
   with the heading tucked under the header, which reads as a broken link. */
.article-band__prose h2 { scroll-margin-block-start: var(--space-2000); }

/* ---------------------------------------------------------------------------
   COLLAPSIBLE CHAPTERS — phones only
   ---------------------------------------------------------------------------
   Built by article-sections.js below 768px; see that file for why it is JS and
   not a server-rendered <details>. Everything here is inert until the script
   adds `--collapsible`, so a script-less phone gets the plain article.

   The heading stays an <h2> and gains a <button> inside it, rather than being
   wrapped in <summary> — the button carries the control, the h2 carries the
   heading, and neither has to pretend to be the other.
   =========================================================================== */

.article-band__prose--collapsible .article-band__chapter-heading {
  margin: 0;
  border-block-start: 1px solid var(--color-border-default);
}

/* The first chapter's rule would sit directly under the article's own opening,
   reading as a stray line rather than a divider between chapters. */
.article-band__prose--collapsible > .article-band__chapter-heading:first-child {
  border-block-start: 0;
}

/* Button reset. The row is the tap target, not the words — a chapter title is
   short and a phone thumb is not. */
.article-band__chapter-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-400);
  width: 100%;
  margin: 0;
  padding-block: var(--space-500);
  padding-inline: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--color-text-strong);
  text-align: start;
  cursor: pointer;
}

/* No focus style here on purpose — base.css already gives every focusable
   element the same :focus-visible ring, and a local override would be one more
   place for the site's focus treatment to diverge. (Same note as the TOC.) */

.article-band__chapter-chevron {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  color: var(--color-text-subdued);
  transition: transform var(--motion-duration-fast) var(--motion-easing-standard);
}

.article-band__chapter-toggle[aria-expanded="true"] .article-band__chapter-chevron {
  transform: rotate(180deg);
}

/* The panel carries `.flow`, so paragraphs inside a chapter keep the spacing
   they had before they were wrapped. */
.article-band__chapter-body { margin-block-start: var(--space-600); }

/* Collapsed, the chapters are a list — the last one needs a closing rule or the
   article ends on an open-ended row. */
.article-band__prose--collapsible {
  border-block-end: 1px solid var(--color-border-default);
}

/* Printing a collapsed article would print a table of contents and no article.
   The attribute is what hides them, so the attribute is what has to be beaten. */
@media print {
  .article-band__chapter-body[hidden] { display: block !important; }
  .article-band__chapter-chevron { display: none; }
}

.article-band__toc-item {
  border-inline-start: 2px solid transparent;
  color: var(--color-text-default);
}

/* The padding sits on the anchor, not the list item, so the whole row is
   clickable rather than just the words. */
.article-band__toc-item > a {
  display: block;
  padding-block: var(--space-300);
  padding-inline-start: var(--space-400);
  color: inherit;
  text-decoration: none;
}

.article-band__toc-item > a:hover { color: var(--color-text-strong); }

/* No focus style here on purpose — base.css already gives every focusable
   element the same :focus-visible ring, and a local override would be one more
   place for the site's focus treatment to diverge. */

/* The chapter currently being read.
   TOKEN NOTE — the design marks this with a white pill on the grey band. There
   is no semantic token for "the active item in a list": `surface.raised` is
   DEPTH (a thing floating above another thing), and a selected list item is not
   floating — borrowing it would be the same misuse as the badge that painted
   itself with `surface.sunken`. Rather than take the nearest token, the state is
   carried by weight and an accent rule, both of which name what they are.
   A `surface.selected` mint is proposed on the reconciliation checklist; when it
   lands, this becomes a background and the design matches exactly. */
.article-band__toc-item.is-current {
  border-inline-start-color: var(--color-accent-default);
  color: var(--color-text-strong);
  font-weight: var(--type-weight-medium);
}

@media (min-width: 1024px) {
  .article-band__inner {
    /*
     * MEASURED — 768 prose · 192 gutter · 320 sidebar, inside the 1280 column.
     *
     * Expressed as FRACTIONS of the measurement rather than as fixed pixels.
     * Fixed columns reproduce the design exactly at full width and overflow
     * everywhere below it: 768 + 192 + 320 needs 1280 of content, which the
     * container does not supply until roughly a 1344px viewport, so every width
     * between here and there pushed the sidebar off-screen and produced a
     * horizontal scrollbar. Caught by measuring at 1280; an earlier check at
     * 1440 had passed, which is exactly why one viewport is not a test.
     *
     * fr preserves the design's PROPORTION (2.40 : 1) at every width, which is
     * the thing that actually matters. Note the absolute 768/320 is never
     * reached: `container--wide` caps at 1280 INCLUDING its padding, so a wide
     * viewport yields 1216 of content and the columns land at 723/301. That is
     * how every other section on this site already behaves — the board grid is
     * 4×280, the featured event 584+48+584 — so the whole page stays coherent.
     * Measure ratios here, not pixels.
     */
    grid-template-columns: minmax(0, 768fr) minmax(0, 320fr);
    gap: clamp(var(--space-1200), 15vw, 192px); /* 15vw == 192 at 1280 */
    justify-content: space-between;
  }

  /* The sidebar tracks the reader down a very long article. `start` is the
     header's height plus a little air, so a sticky sidebar never hides under
     the fixed chrome. */
  .article-band__aside {
    position: sticky;
    top: var(--space-2400);
    align-self: start;
  }
}

/* Between tablet and desktop the two measured columns do not both fit, so the
   grid falls back to proportional. Below that the sidebar drops under the
   article, where a table of contents nobody can click is at least out of the
   way of the thing they came to read. */
@media (min-width: 768px) and (max-width: 1023px) {
  .article-band__inner {
    grid-template-columns: 1fr 260px;
    gap: var(--space-2000);
  }
}

@media (max-width: 767px) {
  .article-band__inner { gap: var(--space-1600); }

  /*
   * The aside moves ABOVE the prose on phones. In source order it follows the
   * article — correct for a sidebar, and fine at every width where it sits
   * beside the text — but in a single column it landed at 96% of a 32-screen
   * page. That put the BYLINE there too: "Written by Charles Buck '69 and
   * Robert Birge '68" was invisible until the reader had finished. An
   * attribution belongs at the top of an article, so this is the honest order
   * on a phone, not a workaround.
   *
   * `order` moves it visually only; the DOM keeps the article first, which is
   * what a screen reader and the reading order follow.
   */
  .article-band__aside { order: -1; }
  .article-band__prose { order: 0; }

  /*
   * The chapter NAV is hidden — but ONLY once the chapters have actually
   * collapsed. With every chapter collapsed (article-sections.js) the headings
   * themselves are the table of contents, and keeping the list as well would be
   * the same six links twice, one above the other.
   *
   * The condition matters. `--collapsible` is added by the script, so if it did
   * not run — blocked, failed, still loading — nothing collapsed, the headings
   * are NOT an index, and this rule correctly does not apply: the nav stays as
   * the only way around a 32-screen page. Hiding it unconditionally would have
   * traded one broken state for a worse one.
   *
   * `~` rather than a descendant selector because the aside is a SIBLING of the
   * prose and follows it in source order — which is exactly why `order: -1`
   * above was needed to move it visually.
   *
   * The note above the nav is untouched either way; that is the byline.
   */
  .article-band__prose--collapsible ~ .article-band__aside .article-band__toc {
    display: none;
  }
}

/* ---------------------------------------------------------------------------
   PAGE HEADER — split-panel mode
   ---------------------------------------------------------------------------
   About, Donate and Contact open with text one half and a photograph bleeding
   off the other (Josh, 2026-07-28). History and News & Events stay text-only.

   The MECHANISM is shared with the feature block — see the SPLIT PANEL header
   in components.css for what is shared, what is per-block, and which file to
   edit for which kind of change. In short:

     · want the About header shorter or narrower, homepage untouched?
       change the two variables below.
     · want Meet the Whiffs changed, header untouched?
       change its variables in home.css.
     · blur anchoring or bleed behaving wrong for BOTH?
       that is mechanism — fix it in components.css.
   --------------------------------------------------------------------------- */

.page-intro--split {
  /* Matches the feature block today, deliberately — the two read as the same
     device and there is no reason yet for them to differ. They are separate
     values precisely so that can change without argument. */
  --ywa-split-cap: 1728px;

  /* SHORTER than the feature block's 30rem. A page header introduces the page
     below it; the feature section is the destination. If the header ever feels
     overbearing on a short page — Contact is the one to watch, it is barely a
     screen of content — this is the number to bring down. */
  --ywa-split-min-height: 24rem;

  padding-block: 0;   /* the panel owns its own height; the band adds none */
}

/* THE COPY COLUMN MUST LINE UP WITH THE REST OF THE PAGE.
   ---------------------------------------------------------------------------
   The panel is capped at 1728 but the site's content container is 1280, so a
   heading that simply sits at the panel's edge lands LEFT of everything below
   it — the board grid, the article, the footer. Measured at 1440: the h1 was at
   x=112 while the section under it started at x=192. That is the bug Josh
   caught, and it is specific to the header; the feature block is a full-bleed
   promotional section and is meant to escape the column.

   THE ALIGNMENT, without using 100vw. Half the content column is 640. Cap the
   copy at 640 and push it to the INNER edge of its half, and its outer edge
   lands exactly on the container's edge at every width — because
   (panel / 2) − 640 is identically the container's offset whether the panel is
   viewport-width or capped:

     panel 1280 → 640 − 640 =   0   container offset   0   ✓
     panel 1440 → 720 − 640 =  80   container offset  80   ✓
     panel 1728 → 864 − 640 = 224   container offset 224   ✓

   `100vw` would have been the obvious tool and is the wrong one: it counts the
   scrollbar, which `scrollbar-gutter: stable` reserves but does not include in
   the layout width — so it would misalign by ~15px on Windows and look correct
   on a Mac. Same class of bug, harder to see. */
.page-intro--split .page-intro__text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-600);
  padding-block: var(--space-2000);
  min-width: 0;

  max-width: 640px;              /* half the 1280 content column */
  margin-inline-start: auto;     /* pinned to the photo side, so the outer edge
                                    falls on the container's edge */
  padding-inline: var(--space-800) var(--space-1600);
}

/* Flipped: the copy is now the right-hand half, so it pins to the other edge
   and the padding mirrors with it. */
.ywa-split--media-start.page-intro--split .page-intro__text {
  margin-inline-start: 0;
  margin-inline-end: auto;
  padding-inline: var(--space-1600) var(--space-800);
}

/* In split mode the copy fills its half, so the text-only measure cap would
   fight it. */
.page-intro--split .page-intro__body { max-width: none; }

@media (max-width: 768px) {
  /* Stacked: the photo is above the text, so the text needs an ordinary
     container gutter rather than the asymmetric desktop one. */
  .page-intro--split .page-intro__text {
    max-width: none;             /* the 640 cap is a two-column device */
    margin-inline: 0;
    padding-inline: var(--space-500);
    padding-block: var(--space-1600);
  }
}
