/* HBA of Dothan — the dynamic surfaces.
 *
 * Loads LAST in the cascade (after motion.css) and styles ONLY what did not exist
 * before: the modal, the news pager, the month calendar, the single-post wrapper,
 * and the two tags that have no measured chip colour. Everything else the
 * shortcodes render — `.hba-eventcard*`, `.hba-eventlist__row`, `.hba-card--topbar`,
 * `.hba-chip--*`, `.hba-grid--3` — is REUSED AS AUTHORED, so those surfaces inherit
 * the measured registers instead of re-deriving them.
 *
 * ⛔ THE ONE THING REUSE DOES NOT CARRY, AND IT WILL BITE ANYONE WHO ADDS A
 * SHORTCODE HERE WITHOUT READING THIS.
 *
 * components.css was authored on top of Bricks' `.brxe-container` defaults —
 * `display:flex; flex-direction:column; align-items:flex-start; width:1100px` —
 * and it says so in six places ("the .brxe-container trap"). Shortcode output is
 * RAW HTML. It is not a Bricks element, so it inherits none of that: an `<a
 * class="hba-card hba-eventcard">` is an INLINE box with no direction, no
 * alignment and no width, and the card collapses into a text run.
 *
 * So §1 below restates direction, alignment AND width for every reused container,
 * scoped to `.hba-dynamic`. Restating only some of them leaves the browser owning
 * the rest — which is the same failure the trap note describes, arriving from the
 * opposite side.
 *
 * ⛔ NO BLANKET HEADING MARGINS (R-9). body.css settles the heading register by
 * SOURCE ORDER on `.hba-section__inner > *` chains, and this file loads after all
 * of it. A `.hba-heading { margin-bottom: … }` here would win everywhere and
 * repaint eleven routes. Every margin below is on a class this file introduced.
 *
 * ⛔ EVERY var(--hba-*) READ HERE IS A SEEDED NAME (P-5). The seeder emits
 * `--hba-amberfill`, not `--hba-amber-fill`; `--hba-inkmuted`, not
 * `--hba-ink-muted`. Values not in design-sync/sync/bricks-builder-settings.json
 * are written as literals with the reason stated, exactly as components.css does
 * for gray-300.
 */

/* =========================================================================
 * 1. THE SHORTCODE HOST — the .brxe-container trap, restated
 * ===================================================================== */

/* The Bricks `shortcode` element renders `<div class="brxe-shortcode hba-dynamic">`
 * as a flex item of `.hba-section__inner`, whose `align-items:flex-start` would
 * shrink-wrap it to its widest line. Third instance of that trap on this build. */
.hba-dynamic {
  width: 100%;
}

/* The section rhythm the replaced markup used to get for free. body.css grants
 * `* + :where(.hba-grid, …)` 40px, and the grid is now one level down inside the
 * shortcode host, so the host claims the register in its place. (0,2,0) beats
 * body.css's (0,1,0) `:where` form, and this file is later regardless. */
.hba-section__inner > * + .hba-dynamic {
  margin-top: 40px;
}

/* ⛔ AND THE SECTION HEAD HAS TO GIVE ITS OWN 40 BACK, OR THE HOST ABOVE DOUBLES IT.
 * MEASURED ON THE CERTIFIED INSTANCE 2026-09-13, not reasoned out: /news' two SS-21
 * sections rendered 48px and 63px taller than live (rhythm R-6 red at TOL 30) and 40
 * of each was this.
 *
 * components.css carries `.hba-sectionhead { margin-bottom: 40px }` and zeroes it with
 * `.hba-sectionhead:has(+ .hba-grid)`, because body.css already grants a following
 * grid its own 40 — one register, granted once, by whichever of the two is in a
 * position to grant it. A shortcode host breaks that arrangement from both ends: the
 * grid is one level down so `:has(+ .hba-grid)` no longer matches, and the rule above
 * adds 40 of its own. Head 40 + host 40 = 80 where every composed section on this site
 * has 40.
 *
 * ⚠ IT WENT UNSEEN UNTIL SS-21 BECAUSE EVERY OTHER DYNAMIC SECTION IS TOLERATED.
 * /events[1..2], /calendar[1..2] and /news[1] all carry runtime row counts and are
 * excused in `rhythm-battery.mjs`'s table, so nothing was comparing their heights to
 * live. SS-21's two sections are the first dynamic sections on this build that are
 * FULLY compared — same 3 and 5 rows, same shapes — and they found it immediately.
 *
 * ⚠ ONE TOLERATED SECTION MOVES THE OTHER WAY AND IT IS RECORDED RATHER THAN TUNED:
 * home[4] "Upcoming Events" measured +15 against live with the doubled gap and -25
 * with the correct one. Live's own band is ~40px taller than our two-card SS-4
 * interim, and the double gap was accidentally paying for that difference. Both
 * numbers are inside TOL and that row is TOLERATED anyway; the register is not bent
 * to keep a compensation nobody designed. */
.hba-sectionhead:has(+ .hba-dynamic) {
  margin-bottom: 0;
}

/* Reused containers, direction + alignment + width restated. */
.hba-dynamic .hba-card,
.hba-dynamic .hba-eventcard {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  color: inherit;
  text-decoration: none;
}
.hba-dynamic .hba-eventlist__row {
  color: inherit;
  text-decoration: none;
}
/* ⛔ SS-21 — THE TWO INDUSTRY-NEWS SHAPES ARE ROWS, AND THE RULE DIRECTLY ABOVE WOULD
 * HAVE TURNED THEM INTO COLUMNS. This is the `.brxe-container` trap arriving from the
 * inside rather than from the browser, and it is worth stating because it is the first
 * time a reused class has been BEATEN by this file instead of un-helped by it.
 *
 * `.hba-newsrow` and `.hba-newscard` both carry `.hba-card` (that is where their panel
 * comes from), and both restate `flex-direction: row` in components.css at (0,1,0)
 * because they are horizontal lockups: icon tile, copy column, trailing glyph. The
 * moment they render inside a shortcode host, `.hba-dynamic .hba-card`'s (0,2,0)
 * `flex-direction: column` outranks them and every HBAA row and NAHB card stacks its
 * 40px icon square on top of its title. The composed markup never met this because a
 * composed card is not inside `.hba-dynamic`.
 *
 * So the two shapes are restated at (0,2,0) as well, which ties on specificity and
 * wins on source order. `align-items` is restated with them for the reason the header
 * gives: half a restatement leaves the browser owning the other half. */
.hba-dynamic .hba-newsrow,
.hba-dynamic .hba-newscard {
  flex-direction: row;
  align-items: flex-start;
}
/* `.hba-eventlist__row` already sets `display:flow-root`, which is what gives the
 * row its own block formatting context — it does not depend on the element being
 * an <a>, which is why SS-9 could change the tag without touching components.css.
 *
 * ⚠ THE AUDIT BEHIND THAT SENTENCE, BECAUSE "IT STILL LOOKS FINE" IS NOT ONE.
 * Every rule components.css carries for these four surfaces —
 * `.hba-eventlist__row` and its `::before`, its child selectors and the
 * `--arrow` / `:has(.hba-cta__arrow)` pair, `.hba-eventcard`, `.hba-card--topbar`
 * and `.hba-calgrid__event` — is a CLASS selector or a descendant of one. Not one
 * of them is written as `a.hba-…` or keys on `:link` / `:visited`, so none of them
 * stopped matching when the tag changed. The three declarations that DID depend on
 * the anchor are `color: inherit` and `text-decoration: none` above (a <div> needs
 * neither, but stating them costs nothing and makes the block tag-agnostic) and
 * the pointer cursor, restated below where it now has to be asked for. */

/* ⛔ THE FOUR MODAL TRIGGERS ARE NO LONGER ANCHORS (ruling SS-9), AND SINCE PR #25
 * REVIEW FINDING 3 THEY ARE REAL <button> ELEMENTS INSIDE THE ITEM HEADING.
 *
 * The shape renders.php now emits is `<h3 class="hba-heading"><button
 * class="hba-cardlink">Title</button></h3>` on the card and row surfaces, and a
 * bare `<button class="hba-calgrid__event …">` on the calendar chip. The container
 * — card, row — carries `data-hba-trigger` and nothing else.
 *
 * ⚠ THE DESIGN DID NOT CHANGE AND MUST NOT. The card is still clickable edge to
 * edge; what changed is which ELEMENT receives that click. `.hba-cardlink::after`
 * stretches the button's hit area over the whole container, which is why
 * `[data-hba-trigger]` has to be `position: relative` — the overlay is positioned
 * against its nearest positioned ancestor, and without that it would resolve
 * against the viewport and cover the page.
 *
 * ⚠ THE BUTTON RESET IS NOT COSMETIC TIDYING. A UA button brings its own font,
 * border, background, padding, centred text and `appearance`, none of which the
 * measured card title has. Left alone it would repaint every item title on three
 * routes in the platform's default 13.3px system stack. `font: inherit` is the
 * single declaration doing most of that work; `text-align: inherit` is the one
 * people forget, because a button's UA text-align is `center`.
 *
 * `display: block; width: 100%` rather than the UA's inline-block: an inline-block
 * sits on a line box and picks up the strut's descender space, which would add a
 * pixel or two under every title and move the card heights the paint battery
 * measures. A block child fills the h3's content box exactly, so the title flows
 * and wraps byte-identically to the plain text it replaced. */
.hba-cardlink {
  appearance: none;
  -webkit-appearance: none;
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-align: inherit;
  text-transform: inherit;
  cursor: pointer;
}
/* The stretched hit area. `inset: 0` against the container, so a click anywhere on
 * the card activates this button and there is exactly ONE control and ONE tab stop
 * per item — the same promise the container-role version made, kept by geometry
 * instead of by an ARIA role.
 *
 * `z-index: 1` because the card's own absolutely-positioned furniture (the event
 * row's `::before` day square and its day/month labels) paints at the same level
 * and would otherwise sit on top of the overlay and swallow clicks over the date. */
[data-hba-trigger] {
  position: relative;
}
.hba-cardlink::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

/* THE FOCUS RING IS THE AFFORDANCE THAT MATTERS. These are the only way into a
 * news post or an event now, so a keyboard visitor who cannot see where they are
 * cannot use the site at all. `:focus-visible` rather than `:focus`, so a mouse
 * click does not leave a ring behind it.
 *
 * ⚠ THE RING GOES ON THE TITLE, NOT ON THE STRETCHED OVERLAY, and that is a
 * deliberate choice rather than the easier one. Ringing the overlay would outline
 * the whole card, which looks tidier and is wrong: `.hba-eventcard` and
 * `.hba-card--topbar` both carry `overflow: hidden` to clip the navy band to the
 * card radius, and an outline drawn on a DESCENDANT is clipped by that — the ring
 * would lose its edges on exactly the two families that need it most. An outline
 * on the button is clipped by nothing, because the button is well inside the
 * padding on every surface.
 *
 * `outline` and not `box-shadow`: box-shadow is dropped entirely in Windows
 * high-contrast mode, and an outline is not. */
.hba-cardlink:focus-visible {
  outline: 2px solid var(--hba-navy, #1e3a5f);
  outline-offset: 2px;
}
/* The event card's title sits on the navy band, where a navy ring is invisible —
 * the same problem the calendar chip has, and it takes the same answer. */
.hba-eventcard__title .hba-cardlink:focus-visible,
.hba-calgrid__event:focus-visible {
  outline: 2px solid var(--hba-amberfill, #fe9a00);
  outline-offset: 2px;
}

/* An empty surface still has to look deliberate. Same 14/22.75 register every
 * card body on this site runs. */
.hba-dynamic__empty {
  font-size: 14px;
  line-height: 22.75px;
  color: var(--hba-inkmuted, #6a7282);
}
.hba-dynamic__empty a {
  color: var(--hba-navy, #1e3a5f);
  text-decoration: underline;
}
.hba-dynamic__grid {
  width: 100%;
}

/* The static twin of `.hba-cta--link`: the affordance INSIDE a card that is
 * itself the anchor, where a nested <a> would be invalid. compose-pages.php has
 * emitted this class on /news since round 2 and NOTHING has ever styled it — a
 * standing P-1 gap (a rendered hba-* class with no rule) that this file closes
 * rather than steps around. It is deliberately declaration-only: the pair
 * `.hba-cta.hba-cta--link` already carries the paint. */
.hba-cta--static {
  cursor: inherit;
}

/* SS-17: the "Learn More" affordance on news cards, featured event cards and
 * /events list rows (owner 2026-09-07 — the cards read as static content, so
 * the click-for-more behaviour gets a visible cue). Paint comes from the
 * `.hba-cta.hba-cta--link` pair like the static twin above; these rules are
 * spacing only. It renders as the card's LAST line: in the float-built
 * eventlist row `display: block` clears the copy column, and in the card
 * bodies the top margin separates it from the excerpt/meta above. */
.hba-card__more {
  display: block;
  margin-top: 12px;
}

/* Categories with no MEASURED chip colour — Membership and Community. See
 * events.php's header: a `.hba-chip` is a promise about a token the adversary can
 * re-derive, and neither of these has one. Deliberately NOT `.hba-chip`, so
 * browser row B-13 keeps testing exactly what it was written to test. */
.hba-eventcat,
.hba-newsgrid__tag {
  display: inline-block;
  font-family: var(--hba-font-display);
  font-size: 12px;
  line-height: 16px;
  font-weight: 600;
  letter-spacing: normal;
  padding: 2px 10px;
  border-radius: var(--hba-radius-pill, 9999px);
  margin: 0;
  color: var(--hba-inkstrong, #374151);
  background: var(--hba-border, #e5e7eb);
}

/* =========================================================================
 * 2. NEWS GRID + PAGER
 *
 * Every page is server-rendered and every card is visible with JS off; the pager
 * ships `hidden` and only dynamic.js takes it off. A control that cannot work is
 * never shown — the same rule the Turnstile strip follows in hba-site.php.
 * ===================================================================== */

.hba-newsgrid {
  width: 100%;
}
/* `[hidden]` alone loses to `.hba-grid { display: grid }` at (0,1,0). This pair is
 * (0,2,0) AND later, so it wins on both counts — an attribute selector beaten by
 * a class is the classic way a JS-hidden grid stays stubbornly visible. */
.hba-newsgrid__page[hidden] {
  display: none;
}
.hba-newsgrid__page + .hba-newsgrid__page {
  margin-top: 24px; /* only reachable with JS off, where the pages stack */
}
.hba-newsgrid__card {
  height: 100%;
}

.hba-pager {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 16px;
  width: 100%;
  margin-top: 32px;
}
.hba-pager[hidden] {
  display: none;
}
.hba-pager__status {
  font-family: var(--hba-font-display);
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.7px;
  color: var(--hba-inkbody, #4a5565);
  margin: 0;
  min-width: 116px;
  text-align: center;
}
.hba-pager__btn,
.hba-calgrid__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--hba-border, #e5e7eb);
  border-radius: var(--hba-radius-input, 8px);
  background: var(--hba-white, #ffffff);
  cursor: pointer;
  transition: border-color var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1)),
              background-color var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.hba-pager__btn:hover:not(:disabled),
.hba-calgrid__btn:hover:not(:disabled) {
  border-color: var(--hba-navy, #1e3a5f);
  background: var(--hba-offwhite, #f5f7fa);
}
/* A disabled control must READ disabled, not merely refuse the click — the end of
 * a pager is information. 0.4 keeps the glyph above P-8's 1.35 floor on white. */
.hba-pager__btn:disabled,
.hba-calgrid__btn:disabled {
  opacity: 0.4;
  cursor: default;
}
.hba-pager__icon {
  display: block;
  width: 18px;
  height: 18px;
  /* The site's arrow glyph, mirrored for `prev`. No chevron exists in the icon
   * set and adding one would be a second arrow vocabulary for one control. */
  background-color: var(--hba-navy, #1e3a5f);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3Cpath d='m12 5 7 7-7 7'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3Cpath d='m12 5 7 7-7 7'/%3E%3C/svg%3E") center / contain no-repeat;
}
.hba-pager__icon--prev {
  transform: scaleX(-1);
}
.hba-pager__icon--next {
  transform: none;
}

/* =========================================================================
 * 3. MONTH CALENDAR
 *
 * In-house, because the Outlook block was dropped permanently (ruling SS-5).
 * Eight months are server-rendered; JS shows one and reveals the nav.
 * ===================================================================== */

.hba-calgrid {
  width: 100%;
}
.hba-calgrid__nav {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 20px;
  width: 100%;
  margin-bottom: 24px;
}
.hba-calgrid__nav[hidden] {
  display: none;
}
.hba-calgrid__label {
  font-size: 24px;
  line-height: 32px;
  font-weight: 600;
  color: var(--hba-charcoal, #0d1b2a);
  margin: 0;
  min-width: 200px;
  text-align: center;
}
/* The per-month heading is the NO-JS title. dynamic.js sets `.is-interactive` on
 * the root, at which point the nav label above carries it and this one would be a
 * duplicate. `display:none` and not a visually-hidden clip, deliberately: R-9
 * measures gaps between rendered boxes, and a 1px clipped heading sitting flush
 * above the "no events" note is a failing pair on a page with nothing wrong. */
.hba-calgrid__monthtitle {
  font-size: 24px;
  line-height: 32px;
  font-weight: 600;
  color: var(--hba-charcoal, #0d1b2a);
  margin: 0 0 16px;
  text-align: center;
}
.hba-calgrid.is-interactive .hba-calgrid__monthtitle {
  display: none;
}
.hba-calgrid__month + .hba-calgrid__month {
  margin-top: 40px;
}
.hba-calgrid__month[hidden] {
  display: none;
}

/* ⛔ MOBILE IS A SCROLL, NOT A REFLOW. A seven-column month cannot become one
 * column and still be a calendar, and `minmax(0, 1fr)` at 390px leaves ~48px
 * cells that cannot hold an event title. So the table keeps a floor width and
 * scrolls INSIDE its own container — the page body never scrolls sideways. The
 * day-name row is inside the same scroller so the two can never drift apart. */
.hba-calgrid__scroll {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.hba-calgrid__table {
  min-width: 660px;
}
.hba-calgrid__dow,
.hba-calgrid__days {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px;
}
.hba-calgrid__dow {
  margin-bottom: 4px;
}
.hba-calgrid__dowcell {
  font-family: var(--hba-font-display);
  font-size: 12px;
  line-height: 16px;
  font-weight: 600;
  letter-spacing: 0.35px;
  text-transform: uppercase;
  color: var(--hba-inkmuted, #6a7282);
  text-align: center;
  padding: 6px 0;
}
.hba-calgrid__cell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
  min-height: 92px;
  padding: 6px;
  background: var(--hba-white, #ffffff);
  border: 1px solid var(--hba-hairline, #f3f4f6);
  border-radius: var(--hba-radius-hairline, 4px);
}
.hba-calgrid__cell--pad {
  background: transparent;
  border-color: transparent;
}
.hba-calgrid__cell.is-today {
  border-color: var(--hba-amberfill, #fe9a00);
  background: var(--hba-ambertint-10, rgba(245, 147, 0, 0.102));
}
.hba-calgrid__daynum {
  font-family: var(--hba-font-display);
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  color: var(--hba-inkbody, #4a5565);
}
.hba-calgrid__cell.is-today .hba-calgrid__daynum {
  color: var(--hba-charcoal, #0d1b2a);
}

/* The day's event. Deliberately NOT `.hba-chip`: browser row B-13 reads every
 * `.hba-chip` on /calendar and asserts its TEXT against the category map parsed
 * from tokens.json. These carry an event TITLE, so wearing that class would fail
 * a correct page — the gate would be right and the markup wrong. */
/* ⚠ THIS IS A REAL <button> SINCE PR #25 FINDING 3, so it needs the UA reset the
 * other three take from `.hba-cardlink`. Four declarations are load-bearing and
 * were NOT here when it was a <span>:
 *   `appearance` + `border: 0` — a UA button paints its own bevel and border over
 *     the measured chip colour. `--closure` re-adds its 1px border below, and the
 *     modifier is later, so it still wins.
 *   `width: 100%` + `box-sizing: border-box` — a <span> with `display: block`
 *     fills the cell; a <button> shrink-to-fits even at `display: block`, and a
 *     chip sized to its own text can never reach `text-overflow: ellipsis`. This
 *     site does not set a global border-box, so it has to be asked for here or the
 *     6px of horizontal padding would push every chip out of its day cell.
 *   `text-align: left` — a UA button centres. The grid's chips are left-aligned.
 * The two paddings are unchanged, so the painted box is byte-identical to the
 * span's: the span's margin box was the cell width, and so is this button's
 * border box. */
.hba-calgrid__event {
  appearance: none;
  -webkit-appearance: none;
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  border: 0;
  text-align: left;
  cursor: pointer;
  font-family: var(--hba-font-text);
  font-size: 11px;
  line-height: 15px;
  font-weight: 600;
  letter-spacing: normal;
  color: var(--hba-white, #ffffff);
  background: var(--hba-navy, #1e3a5f);
  border-radius: var(--hba-radius-hairline, 4px);
  padding: 3px 6px;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: opacity var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.hba-calgrid__event:hover {
  opacity: 0.85;
}
/* The four measured category colours, read from the same tokens the chips read. */
.hba-calgrid__event--board     { background: var(--hba-chipboard, #1e3a5f); }
.hba-calgrid__event--education { background: var(--hba-chipeducation, #fe9a00); }
.hba-calgrid__event--social    { background: var(--hba-chipsocial, #009966); }
.hba-calgrid__event--showcase  { background: var(--hba-chipshowcase, #9810fa); }
.hba-calgrid__event--default   { background: var(--hba-inkbody, #4a5565); }
/* ⛔ CLOSURE READS AS FURNITURE, NOT AS AN EVENT (ruling SS-10). Every other
 * modifier here is a filled, saturated block — that is what makes an event look
 * like something to attend. An office closure is the opposite kind of fact, and
 * painting it in `--default`'s ink grey would have made it look like an event
 * nobody categorised rather than a deliberate class of its own. So it is the one
 * OUTLINED chip on the grid: hairline ground, border, dark ink. It stays fully
 * legible (dark text on near-white clears P-8's 4.5 floor with room to spare) and
 * it stops competing with the coloured events around it.
 *
 * ⚠ NOT A `.hba-chip` AND NOT CLAIMING A TOKEN. `--hba-border` and `--hba-inkstrong`
 * are seeded names already used by `.hba-eventcat` a few blocks up — this reuses
 * the neutral tag's palette rather than inventing a colour, which is the same
 * boundary SS-3 draws and the reason B-13 stays untouched. */
.hba-calgrid__event--closure {
  background: var(--hba-offwhite, #f5f7fa);
  border: 1px solid var(--hba-border, #e5e7eb);
  color: var(--hba-inkstrong, #374151);
  font-weight: 600;
  /* The filled chips are 3px/6px inside no border; matching the box means taking
   * the 1px border out of the padding, or the closure row sits 2px taller than
   * every event beside it in the same cell. */
  padding: 2px 5px;
}

.hba-calgrid__empty {
  font-size: 14px;
  line-height: 22.75px;
  color: var(--hba-inkmuted, #6a7282);
  margin: 16px 0 0;
  text-align: center;
}

/* =========================================================================
 * 4. MODAL
 *
 * ⚠ THIS HEADER USED TO SAY "the card is always a real <a href> ... this is an
 * enhancement layered over a page that already worked". Ruling SS-9 ended that and
 * the sentence is corrected rather than deleted, because it is the assumption three
 * files were written under. The triggers are buttons, the modal is the ONLY
 * rendering of a news post or an event, and the no-JS promise moved to the listing.
 *
 * Content is CLONED from a <template> the server rendered on four of the five
 * surfaces, so nothing is fetched and nothing is built from strings. The member
 * directory is the fifth and the exception: at ~366 rows a template each cost 374.9
 * KiB more markup, so `dynamic.js` builds those from the card's data attributes with
 * `createElement`/`textContent` only. Section 6 carries that surface's own styles;
 * everything in THIS section is shared by both, which is why the built modal
 * reproduces the `--text` layout's class names exactly.
 * ===================================================================== */

/* <template> is display:none by UA rule, but the ELEMENT still carries a class and
 * paint row P-1 counts it. Stated so the row sees a real declaration. */
.hba-modal__tpl {
  display: none;
}

.hba-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.hba-modal[hidden] {
  display: none;
}
.hba-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(13, 27, 42, 0.65); /* charcoal at 65% — no seeded alpha token
                                       * carries a charcoal tint (P-5) */
}
.hba-modal__dialog {
  position: relative;
  width: 100%;
  max-width: 880px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background: var(--hba-white, #ffffff);
  /* ⚠ `cardLarge`, CAMEL CASE. Custom-property names are case-SENSITIVE and the
   * seeder emits `--hba-radius-cardLarge`; the lower-cased spelling resolves to
   * nothing and paints from the literal, which is the exact defect row P-5 was
   * written for (site.css's `--hba-amber-fill` note). */
  border-radius: var(--hba-radius-cardLarge, 16px);
  box-shadow: var(--hba-shadow-dramatic, rgba(0, 0, 0, 0.25) 0px 25px 50px -12px);
  padding: 32px;
}
.hba-modal__close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--hba-radius-pill, 9999px);
  background: var(--hba-offwhite, #f5f7fa);
  cursor: pointer;
  transition: background-color var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.hba-modal__close:hover {
  background: var(--hba-border, #e5e7eb);
}
.hba-modal__closeicon {
  display: block;
  width: 16px;
  height: 16px;
  background-color: var(--hba-charcoal, #0d1b2a);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* The clone target. Stated rather than left implicit: it is a rendered `hba-*`
 * class, so paint row P-1 counts it, and it has to be a full-width block or the
 * grid layout below inherits the dialog's padding box wrongly. */
.hba-modal__content {
  display: block;
  width: 100%;
}

/* Both shapes were asked for by name: image left / text right where an event or
 * post HAS a featured image, centred text where it does not. The layout follows
 * the DATA, decided server-side in renders.php, so neither shape is a media-query
 * accident. */
.hba-modal__layout {
  display: grid;
  gap: 28px;
  align-items: start;
}
.hba-modal__layout--text {
  justify-items: center;
  text-align: center;
}
.hba-modal__media {
  min-width: 0;
}
.hba-modal__img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--hba-radius-card, 12px);
}
.hba-modal__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
}
.hba-modal__layout--text .hba-modal__copy {
  align-items: center;
}
.hba-modal__copy > * + * {
  margin-top: 16px;
}
.hba-modal__title {
  font-size: 28px;
  line-height: 35px;
  font-weight: 700;
  color: var(--hba-charcoal, #0d1b2a);
  margin: 0;
}
.hba-modal__meta {
  display: flex;
  flex-direction: column;
  align-items: inherit;
  gap: 6px;
}
.hba-modal__text {
  font-size: 15px;
  line-height: 24.375px; /* 15 x 1.625, the leading-relaxed the cards run */
  color: var(--hba-inkbody, #4a5565);
}
.hba-modal__text > * + * {
  margin-top: 12px;
}
.hba-modal__text a {
  color: var(--hba-navy, #1e3a5f);
  text-decoration: underline;
}
.hba-modal__date {
  margin: 0;
}
.hba-modal__cta {
  flex: none;
}

/* =========================================================================
 * 4a. AUTHORED-CONTENT BLOCKS — what the office's own body copy renders as
 *
 * ⛔ THIS SECTION EXISTS BECAUSE THE RENDERER ONLY EVER UNDERSTOOD FEATURED
 * IMAGES, AND THE BLOCK EDITOR'S MOST OBVIOUS AFFORDANCE IS NOT ONE.
 *
 * Measured on staging 2026-09-04 (defects P3/P4): within four minutes the same
 * author put a photo in an event's BODY and a photo in a news post's FEATURED
 * IMAGE panel. Both were reasonable; both rendered wrong, by two different
 * mechanisms. The featured one was P1 (fixed in renders.php). The body one landed
 * here — at its intrinsic 640×423, square-cornered, with browser-default <figure>
 * margins, inside a CENTRED-text modal, because `has_post_thumbnail()` was false so
 * the media layout was never chosen. The only thing stopping it overflowing the
 * dialog was Bricks' global `img { max-width: 100% }`.
 *
 * ⛔ AND THE BLOCK LIBRARY IS NOT COMING BACK TO FIX IT. Bricks dequeues
 * `wp-block-library` AND `global-styles` on any page it renders (setup.php,
 * gated on `Helpers::render_with_bricks()`) — which is every composed route and the
 * event single. Measured: zero `block-library` occurrences on /news/, /events/ and
 * /calendar/. That assumption — a Bricks page contains no Gutenberg blocks —
 * stopped being true the moment self-service authoring shipped, and it is why a
 * `core/file` download button rendered as an unreadable dark pill.
 *
 * Re-enqueuing the library was rejected: ~100KB to import a second, complete design
 * system whose defaults would then have to be fought back out of eleven routes. So
 * the handful of block classes the office can realistically produce are styled in
 * HBA's own tokens instead, SCOPED to the two places authored content appears
 * (`.hba-modal__text` and `.hba-single`) so nothing here can reach a composed page.
 *
 * ⚠ IF A BLOCK IS NOT LISTED HERE IT RENDERS UNSTYLED, NOT BROKEN — and that is the
 * intended failure mode. The list covers what the trial run and the flyer workflow
 * actually produce: paragraphs, images, file downloads, lists, quotes, buttons.
 * Adding to it is cheap. Guessing at every block core ships is how this file would
 * become the block library it exists to avoid.
 * ===================================================================== */

.hba-modal__text :where(img),
.hba-single :where(img) {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--hba-radius-card, 12px);
}
/* ⚠ SS-18 — A BLOCK IMAGE DOES NOT FOLLOW `text-align`, AND THE `--text` MODAL IS
 * CENTRED. `.hba-modal__layout--text` sets `justify-items: center; text-align:
 * center` and `.hba-modal__copy` centres its own children, so every line of copy in
 * an image-less modal is centred — but the rule directly above makes an authored
 * body image `display: block`, and a block box ignores `text-align` entirely. It
 * sits flush left under centred copy. Nothing had ever put an image in a `--text`
 * modal, so nothing had ever shown it; the two BBQ & Raffle bodies did the moment
 * they gained the raffle-ticket QR, and a left-flushed code under centred copy
 * reads as breakage rather than as a choice.
 *
 * SCOPED TO `--text` ON PURPOSE, and this is the whole reason it is not simply
 * added to the rule above. The media layout's copy column is `align-items:
 * flex-start` with left-aligned text; an image there should stay flush with the
 * copy it belongs to. Alignment follows the LAYOUT, and the layout is chosen from
 * the DATA in renders.php — so this stays a property of the shape rather than a
 * property of images. `.hba-single` is deliberately not included: SS-9 301s every
 * single, so no authored image ever renders there. */
.hba-modal__layout--text .hba-modal__text :where(img) {
  margin-inline: auto;
}
/* The block editor's own figure margins are a different design system's rhythm.
 * The modal and the single both already own vertical spacing through their
 * `> * + *` chains, so a figure contributes its own and nothing else. */
.hba-modal__text figure,
.hba-single figure {
  margin: 0;
}
.hba-modal__text figcaption,
.hba-single figcaption {
  font-size: 13px;
  line-height: 20px;
  color: var(--hba-inkmuted, #6a7282);
  margin-top: 6px;
  text-align: left;
}
.hba-modal__text .wp-block-image,
.hba-single .wp-block-image {
  margin: 0;
}
/* `is-resized` / width attributes from the editor are honoured up to the measure,
 * never past it — an author dragging an image wider than the dialog is a mistake
 * the page should absorb rather than reproduce. */
.hba-modal__text .wp-block-image img,
.hba-single .wp-block-image img {
  width: auto;
}

/* ── core/file: the download row ─────────────────────────────────────────────
 * The markup is correct and was purely unstyled — `<a class="wp-block-file__button
 * wp-element-button" download>` sitting on top of the filename link as a dark grey
 * pill with effectively invisible label text. This is the block the office will
 * produce most: ruling SS-8 made per-post flyer downloads CONTENT they add in
 * wp-admin, and four flyers are already queued for it. It gets the site's own
 * amber action treatment so it reads as the deliberate control it is. */
.hba-modal__text .wp-block-file,
.hba-single .wp-block-file {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin: 0;
}
.hba-modal__text .wp-block-file a:not(.wp-block-file__button),
.hba-single .wp-block-file a:not(.wp-block-file__button) {
  color: var(--hba-navy, #1e3a5f);
  text-decoration: underline;
}
.hba-modal__text .wp-block-file__button,
.hba-single .wp-block-file__button {
  /* `!important` is load-bearing here and nowhere else in this file: core emits
   * `wp-element-button` with a `background` from the theme.json layer, and on the
   * routes where Bricks dequeues `global-styles` the surviving declaration is an
   * inline-ish UA/plugin default this rule would otherwise tie with. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--hba-font-display);
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: 0.65px;
  text-transform: uppercase;
  text-decoration: none !important;
  color: var(--hba-charcoal, #0d1b2a) !important;
  background: var(--hba-amberfill, #fe9a00) !important;
  border: 0;
  border-radius: var(--hba-radius-input, 8px);
  padding: 8px 16px;
  transition: opacity var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.hba-modal__text .wp-block-file__button:hover,
.hba-single .wp-block-file__button:hover {
  opacity: 0.88;
}

/* ── the remaining blocks the office can produce ─────────────────────────── */
.hba-modal__text :where(ul, ol),
.hba-single :where(ul, ol) {
  padding-left: 22px;
  margin: 0;
}
.hba-modal__text li + li,
.hba-single li + li {
  margin-top: 4px;
}
.hba-modal__text blockquote,
.hba-single blockquote {
  margin: 0;
  padding-left: 16px;
  border-left: 3px solid var(--hba-amberfill, #fe9a00);
  color: var(--hba-inkstrong, #374151);
}
.hba-modal__text blockquote p:last-child,
.hba-single blockquote p:last-child {
  margin-bottom: 0;
}
/* core/buttons — the same amber action as the file download, so an author who
 * reaches for a button block does not get a second button vocabulary. */
.hba-modal__text .wp-block-buttons,
.hba-single .wp-block-buttons {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 12px;
}
.hba-modal__text .wp-block-button__link,
.hba-single .wp-block-button__link {
  display: inline-flex;
  align-items: center;
  font-family: var(--hba-font-display);
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: 0.65px;
  text-transform: uppercase;
  text-decoration: none !important;
  color: var(--hba-charcoal, #0d1b2a) !important;
  background: var(--hba-amberfill, #fe9a00) !important;
  border-radius: var(--hba-radius-input, 8px);
  padding: 8px 16px;
}
/* An embed (the trial run produced a YouTube link) must not push the dialog wide. */
.hba-modal__text :where(iframe, video),
.hba-single :where(iframe, video) {
  max-width: 100%;
}

@media (min-width: 768px) {
  .hba-modal__layout--media {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  }
}

/* Entry motion. motion.css's reduced-motion block already zeroes every duration
 * site-wide via its `*` rule, so this needs no second media query — it is damped
 * by the file that loads before it, which is exactly why that file loads first. */
.hba-modal__dialog {
  animation: hba-modal-in var(--hba-duration-base, 200ms) var(--hba-ease-expressive, cubic-bezier(0.23, 1, 0.32, 1));
}
@keyframes hba-modal-in {
  from { opacity: 0; translate: 0 12px; }
  to   { opacity: 1; translate: none; }
}

/* =========================================================================
 * 5. SINGLE POST / SINGLE EVENT
 *
 * ⚠ THIS IS A FLOOR, NOT A TEMPLATE. With no Bricks template matching a single
 * `post` or `event`, Bricks renders its own fallback around the_content. The
 * authored lane can only reach what the_content returns, so this constrains the
 * measure and puts the site's type ramp on it — enough that a permalink opened
 * from a card or a modal looks intentional. A designed single view is a Bricks
 * template, and that is a separate decision.
 * ===================================================================== */

.hba-single {
  max-width: 760px;
  margin-inline: auto;
  padding: 40px 24px 64px;
  font-family: var(--hba-font-text);
  font-size: 17px;
  line-height: 27.625px; /* 17 x 1.625 */
  color: var(--hba-inkbody, #4a5565);
}
.hba-single > * + * {
  margin-top: 20px;
}
.hba-single h1,
.hba-single h2,
.hba-single h3 {
  font-family: var(--hba-font-display);
  color: var(--hba-charcoal, #0d1b2a);
}
.hba-single h2 { font-size: 28px; line-height: 35px; font-weight: 700; }
.hba-single h3 { font-size: 20px; line-height: 27.5px; font-weight: 600; }
.hba-single img {
  max-width: 100%;
  height: auto;
  border-radius: var(--hba-radius-card, 12px);
}
.hba-single a {
  color: var(--hba-navy, #1e3a5f);
  text-decoration: underline;
}
.hba-single ul,
.hba-single ol {
  padding-left: 22px;
}
.hba-single__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--hba-border, #e5e7eb);
}
.hba-single__meta {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 6px 20px;
}
.hba-single__meta .hba-meta {
  margin: 0;
}
.hba-single__foot {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px;
  padding-top: 24px;
  border-top: 1px solid var(--hba-border, #e5e7eb);
}
.hba-single__foot a {
  text-decoration: none;
}

/* ── single views: the Bricks fallback furniture this site does not want ──────
 * Ruling context: /event/… and post permalinks became public this round (SS-1,
 * SS-6) and every card links to one. Bricks' fallback single template ships a
 * post-meta row and an author box with it.
 *
 * ⚠ CSS IS THE SECOND HALF OF THIS FIX, NEVER THE WHOLE OF IT. The username leak
 * is closed in renders.php with filters, because display:none does not reach a
 * <meta> tag, a JSON-LD graph or a comment endpoint — hiding alone would have
 * left five of seven `webops_admin` hits intact and looked fixed. What is left
 * here is the visible furniture whose markup Bricks prints regardless: the
 * post-meta row resolves the author through `get_user_by()` inside its own
 * dynamic-data provider (`{author_name}`), which no core filter intercepts.
 *
 * The whole row goes, not just its first span. The row is author · date ·
 * comment count, and with comments now CLOSED a "0 comments" label is not
 * merely redundant, it is wrong. Targeting `.item:first-child` would also be a
 * positional selector against markup this repo does not own. */
body.single-post .bricks-post-header .brxe-post-meta,
body.single-event .bricks-post-header .brxe-post-meta,
body.single-post .brxe-post-author,
body.single-event .brxe-post-author {
  display: none;
}

/* =========================================================================
 * 6. MEMBER DIRECTORY (ruling SS-15)
 *
 * ⛔ EVERY REGISTER IN THIS SECTION IS AUTHORED, NOT MEASURED, AND THAT IS A
 * STATEMENT ABOUT PROVENANCE RATHER THAN AN APOLOGY.
 *
 * Everywhere else in this lane a number is traceable to `docs/design/measurements/`
 * — "H p-6", "H gap-4", a pixel-probed corridor. There is nothing to trace here:
 * live's `/find-a-member` is the coming-soon stub compose-pages.php has been
 * carrying, so the captured page contains no directory, no card and no picker to
 * derive a register from. `rhythm-battery.mjs` does not cover the route for the same
 * reason and is not being made to.
 *
 * So every value below is chosen from a step the site ALREADY uses somewhere, and
 * each one says which. That is the honest version of "matches the tokens": it is
 * consistency with the system, not agreement with a capture, and the two must not be
 * written down as if they were the same thing.
 *
 * The brief is the client's: cards that look "like the news articles… but smaller,
 * because there're so many".
 * ===================================================================== */

.hba-memberdir {
  width: 100%;
}

/* ⛔ SHIPS `hidden` FROM PHP AND ONLY `dynamic.js` REVEALS IT — the pager's rule,
 * applied to a control whose failure would be worse. A search box that accepts
 * typing and filters nothing is not a degraded experience, it is a lie about a list
 * of 366 rows. This pair is what makes `[hidden]` actually win: the flex declaration
 * below is a class selector at (0,1,0) and a bare `[hidden]` is (0,1,0) too, so the
 * attribute form is restated to break the tie deliberately rather than by source
 * order. Same trap `.hba-newsgrid__page` documents in section 2. */
.hba-memberdir__controls {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 16px;                /* the site's gap-4 step */
  width: 100%;
  margin-bottom: 24px;      /* the grid gap, so the controls sit on the same rhythm */
}
.hba-memberdir__controls[hidden] {
  display: none;
}

.hba-memberdir__search {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  max-width: 420px;
}

/* ⛔ A REAL, VISIBLE <label> AND NOT A PLACEHOLDER STANDING IN FOR ONE. The
 * placeholder repeats it, deliberately — a placeholder disappears the moment anyone
 * types, and it is not an accessible name, so a search field labelled only by one is
 * an unnamed field to a screen reader and an unlabelled box to anybody who got
 * distracted mid-word. Same 14/20 display register `.hba-pager__status` runs. */
.hba-memberdir__searchlabel {
  font-family: var(--hba-font-display);
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.7px;
  color: var(--hba-inkbody, #4a5565);
  margin-bottom: 6px;
}
.hba-memberdir__input {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  /* 16px is not a typographic choice: iOS Safari zooms the viewport on focus for any
   * input below 16px, which on this page would drop the visitor into a zoomed grid
   * they then have to pinch back out of. B-12 measures overflow, not that — it is
   * simply the wrong thing to do to someone on a phone. */
  font-family: var(--hba-font-text);
  font-size: 16px;
  line-height: 24px;
  color: var(--hba-charcoal, #0d1b2a);
  background: var(--hba-white, #ffffff);
  border: 1px solid var(--hba-border, #e5e7eb);
  border-radius: var(--hba-radius-input, 8px);
  padding: 10px 14px;
  transition: border-color var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.hba-memberdir__input::placeholder {
  color: var(--hba-inkmuted, #6a7282);
}
.hba-memberdir__input:focus-visible {
  outline: 2px solid var(--hba-navy, #1e3a5f);
  outline-offset: 2px;
  border-color: var(--hba-navy, #1e3a5f);
}

/* ⛔ `flex-wrap: wrap` IS THE B-12 FIX AND IT IS LOAD-BEARING, NOT DEFENSIVE.
 * 27 buttons in a row cannot fit a 320px viewport by any sizing: at the 34px below
 * they need over 1,000px. Unwrapped they would push the document wider than the
 * viewport and B-12 fails the route at every captured mobile width — the one gate
 * this section could plausibly turn red. Wrapped, a 320px screen takes 8 per row.
 *
 * ⚠ 34px, NOT THE PAGER'S 40px, AND THE TRADE IS WORTH ONE LINE. 27 targets at 40px
 * is five rows of picker above the grid on a phone. 34px clears WCAG 2.5.8's 24px AA
 * minimum with room, and misses the 44px AAA figure the pager's two buttons happen
 * to meet. Chosen knowingly: a picker so tall it pushes the directory itself off the
 * first screen is its own accessibility problem. */
.hba-memberdir__letters {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 4px;
  width: 100%;
}
.hba-memberdir__letter {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 34px;
  padding: 0 6px;
  font-family: var(--hba-font-display);
  font-size: 13px;
  line-height: 18px;
  font-weight: 600;
  letter-spacing: 0.65px;
  color: var(--hba-inkbody, #4a5565);
  background: var(--hba-white, #ffffff);
  border: 1px solid var(--hba-border, #e5e7eb);
  border-radius: var(--hba-radius-input, 8px);
  cursor: pointer;
  transition: border-color var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1)),
              background-color var(--hba-duration-base, 200ms) var(--hba-ease-standard, cubic-bezier(0.4, 0, 0.2, 1));
}
.hba-memberdir__letter:hover:not(:disabled) {
  border-color: var(--hba-navy, #1e3a5f);
  background: var(--hba-offwhite, #f5f7fa);
}
/* ⛔ THE PRESSED STATE IS KEYED ON `aria-pressed`, NOT ON A CLASS `dynamic.js` ALSO
 * SETS. One source of truth for "which letter is active": if the visual state came
 * from a class, a future edit could set the class and forget the attribute and the
 * picker would look right while announcing nothing — which for this control is the
 * whole of the feedback a screen-reader user gets. Styling the attribute makes the
 * two impossible to separate. */
.hba-memberdir__letter[aria-pressed="true"] {
  color: var(--hba-white, #ffffff);
  background: var(--hba-navy, #1e3a5f);
  border-color: var(--hba-navy, #1e3a5f);
}
/* A letter no member starts with. Same 0.4 the pager's ends use — it must READ
 * disabled rather than merely refuse the click, because "there are no Q companies"
 * is information. */
.hba-memberdir__letter:disabled {
  opacity: 0.4;
  cursor: default;
}
.hba-memberdir__letter:focus-visible {
  outline: 2px solid var(--hba-navy, #1e3a5f);
  outline-offset: 2px;
}

/* The live region. It is OUTSIDE the hidden controls because it is true with
 * JavaScript off, and it keeps its own margin so the count never collapses onto the
 * first row of cards. */
.hba-memberdir__count {
  font-size: 14px;
  line-height: 22.75px;
  color: var(--hba-inkmuted, #6a7282);
  margin: 0 0 16px;
}
/* `.hba-dynamic__empty` carries the paint; this adds only the position, so the empty
 * state cannot drift away from the one every other dynamic surface uses. */
.hba-memberdir__empty {
  margin: 24px 0 0;
  text-align: center;
}
.hba-memberdir__empty[hidden] {
  display: none;
}

/* SS-19 — the coming-soon notice that stands in for the whole directory while the
 * per-member publication approvals are collected (members.php's
 * `HBA_MEMBER_DIRECTORY_PUBLIC`).
 *
 * ⚠ POSITION ONLY, EXACTLY LIKE `.hba-memberdir__empty` DIRECTLY ABOVE. The paint is
 * `.hba-heading` + `.hba-body .hba-dynamic__empty`, which is the register every other
 * empty surface on this site already uses — the notice should look like the site
 * saying "not yet", not like a new component. The one new class exists because a
 * rendered `hba-*` class with no rule is the standing P-1 defect and an unstyled
 * `<h3>` flush against its own paragraph is the standing R-9 one; three declarations
 * close both without inventing a register.
 *
 * The 12px heading gap is the site's gap-3 step (`.hba-card--icon`'s heading margin,
 * components.css). `/find-a-member` is not in rhythm-battery's route set — live has
 * no directory to pair against (SS-15) — so this is authored, not measured, and says
 * so here rather than being recorded as if it agreed with a capture. */
.hba-memberdir__soon {
  width: 100%;
  text-align: center;
}
.hba-memberdir__soon > .hba-heading {
  margin: 0 0 12px;
}
/* While locked, the section's own composed head — eyebrow + "Search the Membership
 * Directory" — is a promise the page can't keep, so it hides for exactly as long as
 * the notice is present. Keyed on the notice marker, NOT on a class of its own:
 * flipping HBA_MEMBER_DIRECTORY_PUBLIC removes the marker and the head comes back by
 * itself, preserving SS-19's "the constant flip is the whole re-launch" property
 * (the composed tree stays untouched either way). `:has()` is already load-bearing
 * in this file and components.css, so no new support floor is introduced. */
.hba-section__inner:has(.hba-dynamic [data-hba-memberdir-soon]) > .hba-sectionhead {
  display: none;
}

/* ⛔ DENSER THAN `.hba-grid--3`, AND IT IS ITS OWN CLASS RATHER THAN A MODIFIER OF
 * IT. `.hba-grid--3` is a MEASURED register — body.css derives its 1-to-3-at-768
 * ladder from seven captured class strings and explains at length why it does not
 * pass through a two-column step. A directory is a different problem (366 short
 * rows, not 3 rich cards) and needs 4-up, so borrowing that class and overriding its
 * breakpoints would quietly make a measured register mean something else on one
 * route. A separate class leaves the measured one alone.
 *
 * The ladder is 1 -> 2 at 768 -> 4 at 1024: the site's own two breakpoints, no third
 * one invented. The gap is 16px — live's `gap-4`, the step body.css records for
 * /news §3 — rather than the 24px `.hba-grid` runs, because the cards are smaller and
 * the client's note was that there are a lot of them. */
.hba-membergrid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  width: 100%;
  text-align: left;
}
@media (min-width: 768px) {
  .hba-membergrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .hba-membergrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* The compact card. `.hba-card` supplies ground, radius and shadow; this narrows the
 * padding from its measured 28px to 20px, which is the same scale one step down
 * (H p-5). Direction and alignment come from `.hba-dynamic .hba-card` in section 1 —
 * the .brxe-container trap applies here exactly as it does to every other shortcode
 * card, and restating them would be a second place to keep them right. */
.hba-membercard {
  height: 100%;
  padding: 20px;
}
/* The card interior's rhythm, which nothing else supplies: body.css's type rhythm is
 * a direct-child chain on `.hba-section__inner > *` and stops at the first nesting
 * level, so without this the tag, the name and the town sit flush at zero — the
 * defect RHYTHM_TRUTH.md is about, in a new card. 8px is the compact step (H
 * space-y-2); the news card's interior runs wider because its content is longer. */
.hba-membercard > * + * {
  margin-top: 8px;
}
/* ⛔ (0,3,0), SO IT CANNOT LOSE. `.hba-dynamic .hba-card` sets `display: flex` at
 * (0,2,0) and a bare `[hidden]` is (0,1,0) — a filtered-out card would stay
 * stubbornly visible and the search would appear to do nothing at all. Section 2
 * documents the same collision on `.hba-newsgrid__page[hidden]`, which resolves it
 * with a (0,2,0) tie broken by source order; this route's rule is written to win
 * outright rather than to depend on where in the file it sits. */
.hba-membergrid .hba-membercard[hidden] {
  display: none;
}
/* 16/22 — text-base on leading-snug, one step under the news card's 20/27.5. This is
 * the "smaller" the client asked for, and it is the only place it is expressed. */
.hba-membercard__title {
  font-size: 16px;
  line-height: 22px;
  font-weight: 700;
  color: var(--hba-charcoal, #0d1b2a);
  margin: 0;
}
/* The town line, in the 14/22.75 card-body register every other card on the site
 * runs its supporting copy at. */
.hba-membercard__place {
  font-size: 14px;
  line-height: 22.75px;
  color: var(--hba-inkmuted, #6a7282);
  margin: 0;
}

/* The member modal's phone and email rows are the only links this site puts inside
 * `.hba-modal__meta`, and until now nothing styled an anchor there — they would have
 * painted as the UA's blue underline in the middle of a dialog that has none. Same
 * treatment `.hba-modal__text a` already gives authored body links. */
.hba-modal__meta a {
  color: var(--hba-navy, #1e3a5f);
  text-decoration: underline;
}
