/*
Theme Name: B Local Asheville & WNC
Theme URI: https://blocalavl.com
Author: Big Sea
Author URI: https://bigsea.co
Description: Block theme for B Local Asheville & WNC. Design system lives in theme.json; page sections ship as editable block patterns.
Requires at least: 6.6
Tested up to: 6.9
Requires PHP: 8.1
Version: 0.1.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: blocal
Tags: full-site-editing, block-patterns, blog
*/

/*
 * Almost nothing belongs in this file.
 *
 * Colors, type, spacing, and layout are defined in theme.json so that the
 * block editor shows the same values an editor can choose from. Adding CSS
 * here creates styling the editor cannot see or control.
 *
 * Only genuinely global rules that theme.json cannot express go below.
 */

/* Respect a reduced-motion preference everywhere. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Visible keyboard focus. The default outline is too easy to lose. */
:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--wp--preset--color--contrast);
  outline-offset: 2px;
}

/*
 * Member and event cards sit in a grid; stretch them to equal height so a card
 * with a missing website field does not leave a ragged row.
 */
.wp-block-post-template.is-layout-grid > li {
  display: flex;
}

.wp-block-post-template.is-layout-grid > li > .wp-block-group {
  flex: 1;
}

/*
 * Logo tiles.
 *
 * Members upload whatever they have: wide horizontal lockups, tall stacked
 * marks, squares, and the occasional 200px screenshot. Left alone, a grid of
 * those looks broken. Every logo therefore gets an identical box and is
 * centered inside it with breathing room.
 *
 * Why a flex box with max-width/max-height rather than the featured-image
 * block's own aspectRatio + object-fit:contain: object-fit scales a small logo
 * UP to fill the box, so a low-resolution upload renders soft and blurry at
 * full tile width. Constraining with max-width and max-height instead means a
 * small logo simply sits smaller, which looks intentional rather than broken.
 *
 * The aspect ratio is the single number to change if the rows feel too tall.
 */
figure.blocal-logo {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 3 / 2;
  padding: clamp(0.75rem, 6%, 1.75rem);
  margin: 0;
  overflow: hidden;
}

/* The block wraps the image in a link when the logo is clickable. */
figure.blocal-logo a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

figure.blocal-logo img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/*
 * A logo standing on its own, as on a member profile.
 *
 * The grid tile treatment is wrong here. With no adjacent tiles there is
 * nothing to line up against, so the fixed 3:2 box and its padding just push
 * the logo inward from the page's left edge — it reads as an accident rather
 * than as spacing. Left-aligned, unpadded, and capped in height instead, so it
 * shares the same left edge as the details below it.
 */
figure.blocal-logo.is-bare {
  aspect-ratio: auto;
  padding: 0;
  justify-content: flex-start;
  overflow: visible;
}

figure.blocal-logo.is-bare img {
  max-height: 130px;
  max-width: 100%;
}

/*
 * Readable measure without centering.
 *
 * A constrained layout would cap the width but also center the result, which is
 * what left the profile's body copy 134px to the right of its own heading. This
 * caps the measure and leaves everything on the container's left edge.
 */
.blocal-prose > *,
p.blocal-prose {
  max-width: 68ch;
}

/*
 * Homepage bands: one left edge, one set of measures.
 *
 * Every band is a full-bleed group whose children start at the page's left
 * edge. What varied before was not the edge alone but the width of everything
 * inside: three sections were on a constrained layout, which caps the width and
 * then CENTERS the result, so their copy began 350px right of the headings
 * above and below them.
 *
 * Capping the measure here instead keeps prose readable and leaves it on the
 * left edge. Grids, columns and query loops are untouched and still span the
 * full 1200.
 *
 * Only h1/h2 and direct-child paragraphs are affected, so card text inside a
 * query loop keeps its own smaller measure.
 */
/*
 * Capped in em rather than rem so the limit tracks the heading's own size: at
 * 56px this is about 1000px and at mobile sizes it is proportionally narrower.
 * A fixed rem cap would have held a 56px headline to the measure of a 24px one.
 */
.blocal-band :is(h1, h2) {
  max-width: 18em;
  text-wrap: balance;
}

.blocal-band > p,
.blocal-band .wp-block-cover__inner-container > p,
.blocal-band .wp-block-column > p {
  /* Retuned again for Libre Baskerville, which sets wider than Apple's
     Baskerville. Measured on the rendered page, not assumed. */
  max-width: 34rem;
}

/*
 * Cancel the centering, not just the width.
 *
 * WordPress's constrained layout centers every direct child with
 * `margin-left: auto !important; margin-right: auto !important`. Capping the
 * width alone therefore produces a narrow box centered in the band: correct
 * measure, wrong position, 344px of margin on each side.
 *
 * !important is required here rather than lazy. Core sets the declaration with
 * !important, so no amount of added specificity beats it, and an override
 * without it looks right in the file and does nothing on the page.
 *
 * The margin is an offset, not zero, because two different rules position
 * content in these bands. A flow child sits at the band's padding edge. An
 * alignwide child centers a 1200px container inside that same box and lands
 * further in. Zeroing the margin lined the headings up with the padding edge
 * and left them 16px adrift of the grids beneath them, and the gap would widen
 * on a larger screen since padding is fixed while the wide offset is not.
 *
 * calc((100% - min(1200px, 100%)) / 2) reproduces the wide container's own
 * offset, so capped text and full-width grids share one edge at any viewport.
 * Below 1200px the expression resolves to 0 and both sit at the padding edge.
 *
 * Text inside a column is unaffected. Columns are flow layout and never had
 * auto margins.
 *
 * The second selector is the cover case. A cover nests its content one level
 * down in .wp-block-cover__inner-container, so a heading placed directly in a
 * cover is not a direct child of the band and the first selector never reaches
 * it. The homepage hero hides this by wrapping its content in an alignwide
 * columns block, which lands on the same offset by another route; a cover with
 * a bare heading in it sat 1.6px left of every band below it.
 */
/*
 * The long-form modules are named here too. They are direct children of a
 * constrained band exactly as the headings are, so without this core centers
 * them and a figure or a points list floats in the middle of a band whose
 * heading is pinned left. Listed explicitly rather than as `> *`, because the
 * section-head group and the columns rows deliberately position themselves.
 */
.blocal-band.is-layout-constrained > :is(h1, h2, h3, p, .wp-block-heading,
  .blocal-points, .blocal-toc, .blocal-figure, .blocal-quote, .blocal-cycle,
  .wp-block-buttons, .wp-block-list, .wp-block-quote, .blocal-callout,
  .blocal-tiers),
.blocal-band.wp-block-cover > .wp-block-cover__inner-container
  > :is(h1, h2, p, .wp-block-heading, .wp-block-buttons) {
  margin-left: calc((100% - min(1200px, 100%)) / 2) !important;
  margin-right: auto !important;
}

/*
 * One gutter for every band.
 *
 * The hero is a core/cover and the rest are core/group, and the two take their
 * horizontal padding from different places: the cover carried 16px while the
 * groups used core's global padding. Above 1200px this was invisible, since the
 * wide container's offset is larger than either and wins. Below it the hero's
 * content sat at 40px and every other section at 24px.
 *
 * Setting the gutter here makes the bands agree at any width, and the offset
 * rule above continues to work because it is measured against whatever the
 * content box turns out to be.
 *
 * The selector names both block types so it outranks .has-global-padding
 * without resorting to !important.
 */
/*
 * The gutter is defined once, in theme.json, and read from there.
 *
 * Restating it here as its own clamp was fine until the spacing scale changed
 * underneath it: the band settled at 40px while root padding moved to 48px, and
 * core pulls an alignfull child out by the ROOT value. The full-bleed parade
 * was dragged 8px past each edge and the page grew a horizontal scrollbar.
 *
 * Reading the variable means any future change to the gutter cannot desync the
 * two again.
 */
.wp-block-group.blocal-band,
.wp-block-cover.blocal-band {
  padding-left: var(--wp--style--root--padding-left);
  padding-right: var(--wp--style--root--padding-right);
}

/*
 * Stop the hero being padded twice.
 *
 * A cover block nests its content in .wp-block-cover__inner-container, and that
 * inner element carries core's global padding of its own. With the band gutter
 * now set on the cover, the hero's content was inset by both: 40px + 24px = 64,
 * putting it 24px right of every other section at every width.
 *
 * The gutter belongs to the band, so the inner container gives its up.
 */
.wp-block-cover.blocal-band > .wp-block-cover__inner-container {
  padding-left: 0;
  padding-right: 0;
}

/* -------------------------------------------------------------------------
 * Event page
 * ---------------------------------------------------------------------- */

/*
 * The photograph is optional and always will be. Some gatherings get shot,
 * some do not, and a layout that assumes an image leaves a hole on half of
 * them. The body carries blocal-has-hero or blocal-no-hero, and the page has a
 * deliberate composition either way rather than one good state and one broken.
 */

/* Cap the hero so a tall upload cannot push the title off the first screen. */
.blocal-event .blocal-event-hero img {
  max-height: 30rem;
  object-fit: cover;
  width: 100%;
}

.blocal-event .blocal-event-masthead {
  margin-top: var(--wp--preset--spacing--40);
}

/*
 * No photograph: the title carries the top of the page.
 *
 * The weight comes from composition rather than a bigger font. A one-off
 * display size would sit outside the type scale in theme.json, and the scale is
 * the thing keeping this site consistent. A rule, real space around it, and a
 * tighter measure give the title presence and stay on the system.
 */
body.blocal-no-hero .blocal-event .blocal-event-masthead {
  border-top: 3px solid var(--wp--preset--color--contrast);
  padding-top: var(--wp--preset--spacing--50);
  padding-bottom: var(--wp--preset--spacing--30);
  margin-top: var(--wp--preset--spacing--50);
}

body.blocal-no-hero .blocal-event .blocal-event-masthead h1 {
  max-width: 22ch;
}

/* The summary panel follows the reader down a long write-up. */
@media (min-width: 782px) {
  .blocal-event .blocal-event-rail {
    position: sticky;
    top: var(--wp--preset--spacing--50);
  }
}

/* Panel labels, set apart from the values they introduce. */
.blocal-event .blocal-rail-label {
  margin-bottom: 0;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 0.6875rem;
}

.blocal-event .blocal-rail-label + p {
  margin-top: 0.25rem;
}

/* The ticket button fills the panel, the way a register control reads. */
.blocal-event .blocal-event-rail .wp-block-button {
  width: 100%;
}

.blocal-event .blocal-event-rail .wp-block-button__link {
  display: block;
  text-align: center;
  width: 100%;
}

/*
 * Event cards without a photograph.
 *
 * The chapter will not have a picture of every gathering, and a card that
 * assumes one is a thumbnail-shaped hole above three lines of text. Rather than
 * wait on photographs that may never arrive, a photo-less card leads with its
 * date, using the same idea as the event page's no-hero state: when the image
 * is missing, something else has to carry.
 *
 * :has() rather than a server-side class because these render inside a Query
 * Loop, where every card shares one piece of markup. Browsers without :has()
 * simply get the plain card, which is the current behavior and not broken.
 *
 * This applies to the events archive, where cards do carry featured images and
 * a missing one is a genuine gap. The homepage uses .blocal-event-row instead,
 * which has no image slot to compensate for.
 */
.blocal-event-card:not(:has(.wp-post-image)) {
  border-top-width: 3px;
  border-top-color: var(--wp--preset--color--contrast);
}

.blocal-event-card:not(:has(.wp-post-image)) .wp-block-blocal-event-date {
  font-size: var(--wp--preset--font-size--large);
  font-weight: 700;
  color: var(--wp--preset--color--contrast);
  margin-bottom: 0.35rem;
}

/*
 * Visually hidden, still announced.
 *
 * Used for the "(opens in a new tab)" note on off-site registration links. A
 * sighted reader gets the cue from context; a screen reader user gets nothing
 * unless it is in the text.
 */
.blocal-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* -------------------------------------------------------------------------
 * Member logo scroller
 * ---------------------------------------------------------------------- */

/*
 * Native overflow scrolling with snap points. Nothing animates on its own, so
 * there is no timer, no duplicated track, and no pause control to find a home
 * for: WCAG 2.2.2 only governs motion that starts by itself.
 */
/*
 * Grid rather than flex so the pause control can take a row of its own beneath
 * the strip without being wedged between an arrow and the logos. Arrow, strip,
 * arrow sit on row one; the control lands on row two, aligned to the same right
 * edge as the arrow above it so it reads as part of the same cluster.
 */
.blocal-parade {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  column-gap: 0.5rem;
}

.blocal-parade__viewport {
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}

/* The arrows and the drag are the affordance; the bar itself is visual noise. */
.blocal-parade__viewport::-webkit-scrollbar {
  display: none;
}

/*
 * A visible ring when the scroller takes keyboard focus. It is focusable so it
 * can be scrolled with the arrow keys, and a focusable thing with no focus
 * style is a trap for anyone not using a mouse.
 */
.blocal-parade__viewport:focus-visible {
  outline: 2px solid var(--wp--preset--color--contrast);
  outline-offset: 4px;
  border-radius: 4px;
}

/*
 * max-content so the list is as wide as the logos in it.
 *
 * A block-level flex container takes the width of the scroller instead, and its
 * tiles simply overflow it — the row looks right either way, but the element
 * then reports the viewport's width rather than the roster's, and the wrap
 * arithmetic that makes the drift seamless is measured off exactly that number.
 */
.blocal-parade__set {
  display: flex;
  width: max-content;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

/*
 * Sized to give the roster presence rather than to fit as many logos on screen
 * as possible. At 15rem about four and a half tiles show at 1200, which is
 * enough to read as a row that continues without turning the members into a
 * ticker of thumbnails.
 */
.blocal-parade__item {
  flex: 0 0 15rem;
  width: 15rem;
  scroll-snap-align: start;
}

.blocal-parade__logo {
  display: flex;
  align-items: center;
  justify-content: center;
  /*
   * Height is set to contain the tallest logo, not chosen for presence.
   *
   * Every logo here is constrained by the tile's width, not its height: the
   * widest-rendering one comes out about 125px tall inside 208px of usable
   * width. Making the box taller than that does not enlarge a single logo, it
   * just banks empty space above and below them, which is what made the row sit
   * so far off the button above it.
   *
   * Presence comes from the horizontal padding instead. Dropping it from 1.75rem
   * to 1rem gives every logo 32px more width to render into.
   */
  height: 8rem;
  padding: 0 1rem;
  background-image: none;
}

/*
 * Logos arrive at wildly different aspect ratios and weights, so each is capped
 * in both directions and centered in an identical box. Without that the row
 * reads as a set of mismatched images rather than one roster.
 */
.blocal-parade__logo img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  transition: transform 160ms ease-out;
}

.blocal-parade__logo:hover img,
.blocal-parade__logo:focus-visible img {
  transform: scale(1.05);
}

/*
 * Arrows sit at the ends of the row, inside the 1200 column, so the whole band
 * keeps one set of edges.
 */
.blocal-parade__nav {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: 1px solid var(--wp--preset--color--rule);
  border-radius: 50%;
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--contrast);
  cursor: pointer;
  transition: border-color 140ms ease-out, background-color 140ms ease-out,
              color 140ms ease-out, opacity 140ms ease-out;
}

.blocal-parade__nav svg {
  width: 0.85rem;
  height: 0.85rem;
  display: block;
}

.blocal-parade__nav:hover:not(:disabled) {
  background: var(--wp--preset--color--contrast);
  border-color: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--base);
}

/*
 * Dimmed rather than hidden at the ends: a control that vanishes shifts the row
 * sideways as you reach each end, which is worse than one that greys out.
 */
.blocal-parade__nav:disabled {
  opacity: 0.25;
  cursor: default;
}

/* Row already fits: the arrows have nothing to do, so they go entirely. */
.blocal-parade.is-static .blocal-parade__nav {
  display: none;
}

/*
 * While the row is drifting it holds two identical sets side by side, so the
 * viewport becomes the flex row and each set keeps its own width.
 */
.blocal-parade.is-marquee .blocal-parade__viewport {
  display: flex;
  /*
   * Snapping fights a scroll position that is being written every frame: the
   * browser keeps yanking the row back to the nearest tile. There are no
   * meaningful stops in a continuous drift anyway.
   */
  scroll-snap-type: none;
}

.blocal-parade.is-marquee .blocal-parade__set {
  flex: 0 0 auto;
}

/*
 * The mechanism WCAG 2.2.2 asks for when something moves on its own for more
 * than five seconds. Hovering, focusing or dragging the row pauses it too, but
 * none of those is a control a visitor can find on purpose — this is.
 *
 * Labelled rather than a bare glyph, and parked in the same corner every time,
 * because the floating icon-only button this replaces read as an artifact
 * someone had left on the page.
 */
.blocal-parade__toggle {
  grid-column: 1 / -1;
  justify-self: end;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  /* 44px tall including the border: a touch target, not a decoration. */
  min-height: 44px;
  margin-top: 0.75rem;
  padding: 0.35rem 0.9rem;
  border: 1px solid var(--wp--preset--color--rule);
  border-radius: 999px;
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--muted);
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1;
  cursor: pointer;
  transition: border-color 140ms ease-out, background-color 140ms ease-out,
              color 140ms ease-out;
}

.blocal-parade__toggle:hover {
  background: var(--wp--preset--color--contrast);
  border-color: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--base);
}

.blocal-parade__toggle svg {
  width: 0.7rem;
  height: 0.7rem;
  display: block;
}

/*
 * One icon, two states: the bars show while it is running, the triangle once it
 * is stopped. Swapping visibility inside a single SVG keeps the button from
 * changing width as the label changes.
 */
.blocal-parade__toggle .blocal-parade__play {
  display: none;
}

.blocal-parade.is-paused .blocal-parade__toggle .blocal-parade__bar {
  display: none;
}

.blocal-parade.is-paused .blocal-parade__toggle .blocal-parade__play {
  display: block;
}

@media (max-width: 640px) {
  .blocal-parade__item {
    flex-basis: 11rem;
    width: 11rem;
  }

  .blocal-parade__logo {
    height: 6.25rem;
    padding: 0 0.75rem;
  }

  .blocal-parade__nav {
    display: none;
  }
}

/*
 * Event status chips.
 *
 * The homepage band mixes what is next with recent history, so each card says
 * which it is. Weighting is deliberate rather than symmetrical: upcoming is
 * filled and high-contrast because it is the thing to act on, past is a quiet
 * outline because it is context.
 */
.blocal-status {
  display: inline-block;
  padding: 0.2rem 0.5rem;
  border-radius: 2px;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.4;
}

.blocal-status--upcoming {
  /*
   * Amber is the palette's designated color for small elements, and a status
   * chip is the smallest element on the page. Its black label reads at 11.02:1;
   * white on this amber would be 1.91:1 and fail at any size.
   */
  background: var(--wp--preset--color--accent);
  color: var(--wp--preset--color--contrast);
}

.blocal-status--past {
  border: 1px solid var(--wp--preset--color--rule);
  color: var(--wp--preset--color--muted);
}

.wp-block-blocal-event-status {
  margin-bottom: 0.4rem;
}

/*
 * The card carrying the next event takes a darker frame. Only the border
 * color changes, so this composes with the photo-less card's heavier top rule
 * instead of fighting it.
 */
.blocal-event-card:has(.blocal-status--upcoming) {
  /*
   * !important is required, not lazy. The card's border color is set inline by
   * the block's own border support, and an inline style outranks every selector
   * regardless of specificity. Without it this rule reads as correct in the file
   * and does nothing on the page.
   */
  border-color: var(--wp--preset--color--contrast) !important;
}

/*
 * Upcoming event cards: the title takes the space the photograph would have.
 *
 * An upcoming event never has a picture. The chapter photographs the gathering,
 * not the announcement, so images arrive only after the event has happened.
 * Rather than leave a thumbnail-shaped hole above three lines of text, the title
 * scales up to fill it. Same idea as the event page's no-hero state.
 *
 * !important is required: WordPress emits its font-size preset classes with
 * !important, so has-large-font-size on the title block outranks any selector
 * without it. This is the third rule in this stylesheet that needs it for that
 * class of reason; each one says so rather than leaving it to look arbitrary.
 */
.blocal-event-card:has(.blocal-status--upcoming) .wp-block-post-title {
  /* Not the x-large preset: that tier is now band-heading scale. */
  font-size: 1.375rem !important;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin-top: 0.35rem;
}

/*
 * With the title carrying, the date steps back down. The photo-less rule above
 * enlarges it, which is right when the date is the strongest thing on the card
 * and wrong when it is competing with a 36px headline.
 */
.blocal-event-card:has(.blocal-status--upcoming) .wp-block-blocal-event-date {
  font-size: var(--wp--preset--font-size--small);
}

/* -------------------------------------------------------------------------
 * Full-bleed bands butt against each other
 * ---------------------------------------------------------------------- */

/*
 * Core gives every block in a flow layout a top margin of one block-gap. On a
 * page built from full-width colored bands, that margin is not breathing room
 * between paragraphs, it is a strip of page background showing between two
 * bands, which reads as a seam. Every band sets its own vertical padding, so
 * the margin has nothing left to do.
 *
 * The same gap sits between the header, main and footer, so those go too.
 *
 * The selectors are two classes deep on purpose. Core's rule is
 * `:root :where(.is-layout-flow) > *`, and :where() contributes no specificity,
 * so a single class would tie and depend on stylesheet order.
 */
.wp-site-blocks > *,
.entry-content > .blocal-band,
.wp-block-post-content > .blocal-band {
  margin-block-start: 0;
}

/* -------------------------------------------------------------------------
 * Homepage events list
 * ---------------------------------------------------------------------- */

/*
 * Rows divided by rules rather than cards in a grid. Padding sets the rhythm,
 * so the template's own gap is removed and the rule sits flush between rows.
 * A top rule on every row puts a line above the first and none after the last.
 */
.blocal-events-list .wp-block-post-template {
  gap: 0;
  list-style: none;
  padding: 0;
  margin: 0;
}

/*
 * Date in its own column, details beside it.
 *
 * A past row has no date column at all: the block renders nothing once the
 * event has happened, so the flex row is left with one child and the details
 * start at the edge. That is why this is a flex row rather than a grid with a
 * fixed first track, which would hold an empty column open on every past row.
 *
 * The resulting step between the two row types is intended, not a leftover.
 * Upcoming events are indented behind their date; past ones sit flush, so the
 * list reads as two tiers at a glance rather than one undifferentiated stack.
 * Do not "fix" it by reserving the empty column.
 */
.blocal-event-row {
  display: flex;
  align-items: flex-start;
  gap: var(--wp--preset--spacing--30);
  border-top: 1px solid rgba(255, 255, 255, 0.4);
  padding-top: var(--wp--preset--spacing--40);
  padding-bottom: var(--wp--preset--spacing--40);
}

.blocal-event-row__body {
  flex: 1 1 auto;
  min-width: 0;
}

.blocal-event-row .wp-block-post-title {
  margin: 0.3rem 0 0.2rem;
  line-height: 1.2;
}

/* The whole row is a link target; the underline arrives on interaction. */
.blocal-event-row .wp-block-post-title a {
  text-decoration: none;
}

.blocal-event-row .wp-block-post-title a:hover,
.blocal-event-row .wp-block-post-title a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/*
 * The date steps back from the title without leaving the palette: softened
 * white on this blue is 6.6:1, where a grey would be a color the palette does
 * not contain.
 */
.blocal-event-row .wp-block-blocal-event-date {
  color: rgba(255, 255, 255, 0.86);
  margin: 0;
}

/*
 * The next event still leads, but the date now does the work the oversized
 * title used to. The title steps down to x-large: two elements at display
 * scale in one row is not a hierarchy, it is a collision.
 *
 * !important because WordPress emits its font-size preset classes with it, so
 * has-large-font-size on the title block outranks any selector without it.
 */
/*
 * Keyed on the date, not on a chip.
 *
 * This used to select :has(.blocal-status--upcoming). Suppressing the Upcoming
 * chip left the selector matching nothing, so upcoming titles silently fell
 * back to the same size as past ones. The date column is now what marks an
 * upcoming row, so it is what the rule tests for.
 */
.blocal-event-row:has(.blocal-bigdate) .wp-block-post-title {
  /* Not the x-large preset: that tier is band-heading scale. */
  font-size: clamp(1.5rem, 2.4vw, 2rem) !important;
  line-height: 1.15;
  letter-spacing: -0.015em;
}

/*
 * Chips on a dark band. The amber fill still reads at 11.02:1 with its black
 * label, but the past chip's outline is drawn from black tokens that vanish
 * here, so it takes white equivalents.
 */
.blocal-event-row .blocal-status--past {
  border-color: rgba(255, 255, 255, 0.5);
  color: rgba(255, 255, 255, 0.86);
}

/* -------------------------------------------------------------------------
 * Statement headline
 * ---------------------------------------------------------------------- */

/*
 * Display type needs its leading pulled in. At 3.25rem the theme's inherited
 * line-height leaves the two lines floating apart, which reads as two separate
 * claims rather than one parallel statement.
 */
.blocal-statement {
  line-height: 0.98;
  letter-spacing: -0.04em;
  text-wrap: balance;
  max-width: 22em;
}

/*
 * The emphasis is the entire point of the sentence pair: in the world versus
 * for the world. Italic alone is a weak signal at this weight, so the two words
 * also lighten slightly, which reads as a shift in voice rather than decoration.
 */
.blocal-statement em {
  /*
   * The two pivot words drop out of the heading sans into the body serif. It is
   * a change of voice, not of emphasis, so it reads as an aside inside the
   * claim rather than as a louder version of it.
   *
   * Weight is pinned at 400: Libre Baskerville tops out at 700 and the heading
   * around it is set at 800, so an inherited weight would make the browser
   * synthesise a bold that the family does not contain.
   *
   * Tracking is relaxed too. The -0.04em on the heading is there to close up
   * Inter at display size; the same value on a serif italic jams the letters
   * into each other.
   */
  font-family: var(--wp--preset--font-family--body);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.005em;
  padding-inline: 0.04em;
}

/* -------------------------------------------------------------------------
 * The date as the graphic
 * ---------------------------------------------------------------------- */

/*
 * Upcoming events never carry a photograph, so the date takes the space one
 * would have occupied. It is the only field guaranteed to be both present and
 * short, and it is what a visitor is scanning the list for.
 *
 * Sized in its own clamp rather than a preset: this is display furniture, not
 * body copy, and it should not inherit a scale meant for reading.
 */
.blocal-bigdate {
  display: flex;
  align-items: baseline;
  gap: 0.25em;
  margin: 0 0 0.15rem;
  font-size: clamp(2.25rem, 5vw, 3.5rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

/*
 * The month sets the reading order but the day is the thing being looked up, so
 * the month steps back to about two thirds and the day keeps full scale.
 */
.blocal-bigdate__month {
  font-size: 0.62em;
  letter-spacing: 0.02em;
}

/*
 * On the blue band the date is the brightest element after the chip; amber
 * would fight the chip for attention, so it stays white and wins on size alone.
 */
.blocal-event-row .blocal-bigdate {
  color: var(--wp--preset--color--base);
}

/* On the archive the cards sit on white, so the date takes the ink color. */
.blocal-event-card .blocal-bigdate {
  color: var(--wp--preset--color--contrast);
}

/* -------------------------------------------------------------------------
 * Heading scale
 * ---------------------------------------------------------------------- */

/*
 * Helvetica is spaced for setting text, not headlines. At 56px and above that
 * spacing reads as slack, so the display tiers pull tracking in further than
 * the global heading value and stack their lines almost flush.
 *
 * text-wrap: balance keeps a three-word heading from dropping one orphan word
 * onto a second line, which at this scale is very visible.
 */
.blocal-band :is(h1, h2).has-x-large-font-size,
.blocal-band :is(h1, h2).has-xx-large-font-size,
.blocal-band :is(h1, h2).has-display-font-size,
.wp-block-blocal-member-count.has-x-large-font-size {
  letter-spacing: -0.04em;
  line-height: 0.95;
}

/*
 * Small headings are the exception: the footer's 11px uppercase labels and the
 * event page's section heads need their tracking back, since negative spacing
 * on small caps closes the counters and hurts legibility.
 *
 * Restricted to headings. Written as bare font-size classes this also caught
 * every paragraph carrying a size preset, and set body copy at 1.25 leading:
 * the hero lede was rendering at 22.5px on 18px type, which is tight enough to
 * read as a caption rather than a paragraph.
 */
:is(h3, h4, h5, h6),
:is(h1, h2, h3, h4, h5, h6):is(.has-large-font-size, .has-medium-font-size, .has-small-font-size) {
  letter-spacing: normal;
  line-height: 1.25;
}

/* -------------------------------------------------------------------------
 * Furniture stays on the sans
 * ---------------------------------------------------------------------- */

/*
 * Baskerville is the reading face, not the labeling one. Dates, status chips
 * and the display date are furniture: short, small, often reversed out of a
 * color, and in a serif at 11-13px the thins go weak and the counters close
 * up. They follow the headings and the buttons onto Helvetica.
 *
 * The display date especially: it is a graphic, and it was inheriting the
 * body serif, which is the one thing it should never have been set in.
 */
.blocal-bigdate,
.blocal-status,
.wp-block-blocal-event-date,
.blocal-parade__nav {
  font-family: var(--wp--preset--font-family--display);
}

.blocal-bigdate {
  font-weight: 700;
  letter-spacing: -0.03em;
}

.blocal-status {
  letter-spacing: 0.08em;
}

/* -------------------------------------------------------------------------
 * Hand-drawn underlines
 * ---------------------------------------------------------------------- */

/*
 * A drawn stroke rather than text-decoration. The palette reserves amber for
 * "button background and smaller element colors only", and an underline is the
 * smallest element on the page, so this is the one place the accent can appear
 * in running text without breaking the rule.
 *
 * The stroke is an inline SVG with preserveAspectRatio="none", so it stretches
 * to whatever width the link happens to be instead of tiling into a repeat that
 * would read as a pattern. box-decoration-break: clone puts a stroke under each
 * line of a link that wraps.
 */
:root {
  --blocal-brush: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%208%22%20preserveAspectRatio%3D%22none%22%3E%3Cpath%20d%3D%22M0.6%2C5.4C14%2C2.4%2034%2C6.6%2051%2C4.1C67%2C1.9%2084%2C5.6%2099.4%2C2.9%22%20fill%3D%22none%22%20stroke%3D%22%23ffaa19%22%20stroke-width%3D%222.4%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  --blocal-brush-bold: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%208%22%20preserveAspectRatio%3D%22none%22%3E%3Cpath%20d%3D%22M0.6%2C5.2C14%2C1.9%2034%2C6.4%2051%2C3.7C67%2C1.3%2084%2C5.4%2099.4%2C2.4%22%20fill%3D%22none%22%20stroke%3D%22%23ffaa19%22%20stroke-width%3D%223.6%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
  --blocal-swoosh: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%2013%22%20preserveAspectRatio%3D%22none%22%3E%3Cpath%20d%3D%22M1.2%2C8.1C18%2C3.4%2045%2C9.2%2067%2C5.2C81%2C2.8%2091%2C3.9%2098.6%2C6.1L97.6%2C9.8C90%2C7.5%2082%2C6.4%2067%2C8.9C45%2C12.9%2018%2C7.1%201.9%2C11.2Z%22%20fill%3D%22%23ffaa19%22%2F%3E%3C%2Fsvg%3E");
}

.blocal-band p a,
.blocal-prose a,
.wp-block-post-excerpt a,
.wp-block-list a {
  text-decoration: none;
  background-image: var(--blocal-brush);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 0.34em;
  padding-bottom: 0.12em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  transition: background-size 120ms ease-out, background-image 120ms ease-out;
}

/*
 * Hover thickens the stroke rather than removing it. The previous rule dropped
 * the underline on hover, which takes affordance away at exactly the moment the
 * visitor is deciding whether the thing is clickable.
 */
.blocal-band p a:hover,
.blocal-prose a:hover,
.wp-block-post-excerpt a:hover,
.wp-block-list a:hover {
  background-image: var(--blocal-brush-bold);
  background-size: 100% 0.42em;
}

/*
 * Reversed out of a dark band the amber still clears its contrast, so the same
 * stroke works. Only the text color changes, and that is inherited.
 */
/*
 * Scoped to paragraph links, not every descendant anchor.
 *
 * The earlier form was `.has-surface-blue-background-color a`, which scores a
 * class plus a type and therefore outranked the `.wp-block-button__link` reset
 * below no matter what order they appeared in. Buttons on the dark bands were
 * picking up both the stroke and its padding-bottom, which pushed the label off
 * center inside the button.
 */
.has-surface-blue-background-color p:not(.wp-block-site-title) a,
.has-surface-inverse-background-color p:not(.wp-block-site-title) a,
.wp-block-cover p:not(.wp-block-site-title) a,
footer p:not(.wp-block-site-title) a {
  text-decoration: none;
  background-image: var(--blocal-brush);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 0.34em;
  padding-bottom: 0.12em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  transition: background-size 120ms ease-out;
}

.has-surface-blue-background-color p:not(.wp-block-site-title) a:hover,
.has-surface-inverse-background-color p:not(.wp-block-site-title) a:hover,
.wp-block-cover p:not(.wp-block-site-title) a:hover,
footer p:not(.wp-block-site-title) a:hover {
  background-image: var(--blocal-brush-bold);
  background-size: 100% 0.42em;
}

/* Buttons carry their own affordance and must never inherit the stroke. */
/*
 * A backstop only. Nothing above should reach these any more, but a stroke
 * under an icon or a button label is bad enough to be worth the guard.
 *
 * It deliberately does NOT touch padding: setting padding-bottom here overrode
 * the button's own 0.7rem and pushed every label off center.
 */
.wp-block-button .wp-block-button__link,
.wp-block-navigation-item__content,
.wp-block-site-title a,
.wp-block-post-title a,
.blocal-logo a,
.wp-block-social-link a {
  background-image: none;
}

/*
 * The statement's emphasis takes the brush mark from the reference. The two
 * italic words are the whole argument of the sentence, in the world versus for
 * the world, so the mark lands on the pivot rather than decorating at random.
 */
.blocal-statement em {
  background-image: var(--blocal-swoosh);
  background-repeat: no-repeat;
  background-position: 0 92%;
  background-size: 100% 0.28em;
  padding-bottom: 0.06em;
}

/* -------------------------------------------------------------------------
 * Hover states
 * ---------------------------------------------------------------------- */

/*
 * Buttons had no hover state at all. This inverts them: the amber and the ink
 * swap places. It stays inside the palette exactly, and black-on-amber and
 * amber-on-black are the same 11.02:1 pair read in either direction, so the
 * hover is as legible as the resting state.
 */
.wp-block-button__link {
  transition: background-color 140ms ease-out, color 140ms ease-out,
              border-color 140ms ease-out, transform 140ms ease-out;
}

.wp-block-button:not(.is-style-outline) .wp-block-button__link:hover,
.wp-block-button:not(.is-style-outline) .wp-block-button__link:focus-visible {
  background-color: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--accent);
  transform: translateY(-1px);
}

/* The outline button fills in on hover rather than inverting. */
.wp-block-button.is-style-outline .wp-block-button__link:hover,
.wp-block-button.is-style-outline .wp-block-button__link:focus-visible {
  background-color: var(--wp--preset--color--contrast);
  color: var(--wp--preset--color--base);
  transform: translateY(-1px);
}

/*
 * On the dark bands the outline button is drawn in white.
 *
 * This rule is what makes that true. The hover rule below has always assumed
 * it, but nothing declared it: every dark-band outline button in the patterns
 * happened to carry an inline color, so the assumption held by accident. The
 * first one written without inline styles came out with core's black label on
 * forest green, about 2.3:1 against a 4.5:1 requirement, and looked like a
 * disabled control.
 *
 * Declared here rather than inline on each button so the next one is right
 * without anyone remembering. Inline styles on the existing buttons still win,
 * so nothing already drawn moves.
 */
.has-surface-blue-background-color .wp-block-button.is-style-outline .wp-block-button__link,
.has-surface-inverse-background-color .wp-block-button.is-style-outline .wp-block-button__link,
.has-surface-violet-background-color .wp-block-button.is-style-outline .wp-block-button__link,
footer .wp-block-button.is-style-outline .wp-block-button__link,
.wp-block-cover .wp-block-button.is-style-outline .wp-block-button__link {
  color: var(--wp--preset--color--base);
  border-color: rgba(255, 255, 255, 0.5);
}

/*
 * Filling it with black would leave a black shape on a dark ground, so it fills
 * with amber instead, the same move the solid buttons make.
 */
.has-surface-blue-background-color .wp-block-button.is-style-outline .wp-block-button__link:hover,
.has-surface-inverse-background-color .wp-block-button.is-style-outline .wp-block-button__link:hover,
footer .wp-block-button.is-style-outline .wp-block-button__link:hover,
.wp-block-cover .wp-block-button.is-style-outline .wp-block-button__link:hover {
  background-color: var(--wp--preset--color--accent);
  border-color: var(--wp--preset--color--accent);
  color: var(--wp--preset--color--contrast);
}

/*
 * Navigation. The brush mark is the site's link signal, so it appears here too,
 * but only on hover: eight permanently underlined items would be noise.
 */
.wp-block-navigation-item__content {
  position: relative;
  padding-bottom: 0.15em;
  background-image: none;
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 0;
  transition: background-size 140ms ease-out;
}

.wp-block-navigation-item__content:hover,
.wp-block-navigation-item__content:focus-visible,
.current-menu-item .wp-block-navigation-item__content {
  background-image: var(--blocal-brush);
  background-size: 100% 0.3em;
}

/*
 * Event rows. The whole row is the target, so the whole row responds: the
 * ground lifts slightly and the title takes the stroke.
 */
.blocal-event-row {
  transition: background-color 140ms ease-out;
}

.blocal-event-row:hover {
  background-color: rgba(255, 255, 255, 0.06);
}

.blocal-event-row .wp-block-post-title a {
  transition: background-size 140ms ease-out;
  background-image: none;
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 0;
  padding-bottom: 0.1em;
}

.blocal-event-row .wp-block-post-title a:hover,
.blocal-event-row .wp-block-post-title a:focus-visible {
  text-decoration: none;
  background-image: var(--blocal-brush-bold);
  background-size: 100% 0.26em;
}

/*
 * Directory and event cards lift on hover. Small movement and a firmer border,
 * enough to say the card is a target without the card jumping.
 */
.blocal-event-card,
.wp-block-post .blocal-logo,
.blocal-member-card {
  transition: transform 140ms ease-out, border-color 140ms ease-out,
              box-shadow 140ms ease-out;
}

.blocal-event-card:hover,
.blocal-member-card:hover {
  transform: translateY(-2px);
  border-color: var(--wp--preset--color--contrast);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}

/*
 * Member logos sit at reduced weight in the parade and come up to full on
 * hover, which also pauses the animation via the existing rule.
 */
.blocal-parade .blocal-logo,
.blocal-parade__set a {
  transition: transform 160ms ease-out, opacity 160ms ease-out;
}

.blocal-parade__set a:hover .blocal-logo,
.blocal-parade__set a:focus-visible .blocal-logo {
  transform: scale(1.05);
}

/* The dismiss control on the announcement bar had no hover either. */
.blocal-announcement-bar__dismiss {
  transition: opacity 140ms ease-out, transform 140ms ease-out;
  opacity: 0.75;
}

.blocal-announcement-bar__dismiss:hover,
.blocal-announcement-bar__dismiss:focus-visible {
  opacity: 1;
  transform: scale(1.15);
}

/* -------------------------------------------------------------------------
 * Taking the edge off
 * ---------------------------------------------------------------------- */

/*
 * Not a redesign, just less of a grid of rectangles: a slightly softer corner
 * on the things that read as boxes, and logo tiles that are no longer perfect
 * squares. Everything here is 2-6px; at this scale it registers as warmth
 * rather than as rounding.
 */
.blocal-event-card,
.blocal-member-card,
.blocal-logo,
.blocal-status {
  border-radius: 6px;
}

.wp-block-button__link {
  border-radius: 5px;
}

/*
 * Reduced motion: every transform above is decorative. Color changes stay,
 * because those are the affordance and removing them would leave hover states
 * that do nothing.
 */
@media (prefers-reduced-motion: reduce) {
  .wp-block-button__link:hover,
  .wp-block-button__link:focus-visible,
  .blocal-event-card:hover,
  .blocal-member-card:hover,
  .blocal-parade__set a:hover .blocal-logo,
  .blocal-announcement-bar__dismiss:hover {
    transform: none;
  }
}

/*
 * Hero lede. Sits between a 96px headline and the buttons, so it takes more
 * space above than the default paragraph rhythm gives it.
 */
.blocal-hero-lede {
  margin-top: 1.1rem;
  max-width: 34rem;
}

/* -------------------------------------------------------------------------
 * Botanical bleed
 * ---------------------------------------------------------------------- */

/*
 * A large illustration set into the right of a band, running past its bottom
 * edge so the seam between two sections is broken rather than ruled.
 *
 * The band takes a stacking context of its own so the overflowing part paints
 * above the band that follows. Inside that context the pseudo-element sits at
 * z-index -1, which puts it above the band's own background but below its text,
 * so the copy stays fully legible over it.
 *
 * The illustration lives in the theme rather than the media library. It is a
 * brand asset, not content, and referencing an uploads URL from a stylesheet
 * breaks the moment anyone re-uploads the file.
 */
.blocal-bloom {
  position: relative;
  z-index: 1;

  /*
   * Declared on the band rather than on its ::after, so the copy inside the
   * band can be held clear of the drawing by the same number that sizes it.
   * The pseudo-element reads it by inheritance and draws exactly as before.
   */
  --bloom-w: min(38rem, 48vw);
}

.blocal-bloom::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;

  /*
   * Anchored to the right edge rather than past it. Bleeding sideways would
   * widen the document, and overflow cannot be clipped on one axis alone:
   * hiding overflow-x forces overflow-y to auto and would cut off the very
   * bleed this is for.
   */
  /*
   * Pinned to the band's top edge, not sized from it. A square box anchored
   * below the bottom extended past the top as well, because this band is only
   * about 340px tall, and the artwork ran up over the hero photograph above.
   *
   * A background never paints outside its own box, so top:0 crops the drawing
   * cleanly along the band's top edge, and the size is stated absolutely rather
   * than as `contain` so the flower stays large no matter how short the band
   * gets when its copy is edited.
   */
  top: 0;
  right: 0;
  width: var(--bloom-w);

  /*
   * WebP with a PNG fallback, declared as two properties rather than one
   * image-set so browsers without image-set keep the PNG instead of nothing.
   * Only the chosen file is fetched. The painting is 105KB as WebP against
   * 1034KB as the PNG it arrived as, which is the difference between a
   * decorative flourish and a page-weight problem.
   */
  background-image: url("assets/img/rhododendron.png");
  background-image: image-set(
    url("assets/img/rhododendron.webp") type("image/webp"),
    url("assets/img/rhododendron.png") type("image/png")
  );
  background-repeat: no-repeat;
  background-position: right bottom;
  background-size: var(--bloom-w) auto;

  /*
   * The bleed is measured from the artwork, not chosen by eye.
   *
   * Measured from the artwork rather than chosen by eye. This painting runs its
   * ink to both the top and bottom edges of the file, unlike the line drawing it
   * replaced, which carried 19.2% of dead space underneath and needed 0.31 here
   * just to reach the band edge. With no margin to clear, the offset is the
   * bleed: 0.12 of the box shows below the band.
   *
   * Expressed against --bloom-w rather than in rem so it stays proportional at
   * every width instead of being correct only at the one it was tuned at.
   */
  bottom: calc(var(--bloom-w) * -0.12);
}

/*
 * Below the point where the copy would sit under the flower, it goes. The
 * artwork is decorative; on a narrow screen it would be a texture behind text
 * rather than an illustration beside it.
 */
@media (max-width: 781px) {
  .blocal-bloom::after {
    display: none;
  }
}

@media (min-width: 782px) and (max-width: 1080px) {
  .blocal-bloom {
    --bloom-w: 34vw;
  }
}

/*
 * The section a bloom bleeds into paints above it.
 *
 * The bloom's band carries z-index 1 so its overflow clears the band below,
 * which also puts the artwork above that band's text. At 1440 the leaves and
 * the "Meet the 15 Certified B Corps" heading already share about 18px of row,
 * and any longer heading or shorter band would put a drawing straight through
 * the words.
 *
 * Raising the following band restores the order: its text over the artwork,
 * the artwork over the page.
 *
 * This works only while that band has no background of its own. Giving it one
 * would paint over the bleed and hide it completely, so a bloom belongs above a
 * band that sits on the page ground.
 */
.blocal-bloom + .blocal-band {
  position: relative;
  z-index: 2;
}

/* -------------------------------------------------------------------------
 * Section head: title with a link alongside
 * ---------------------------------------------------------------------- */

/*
 * The heading and its call to action share a row on a wide screen and stack on
 * a narrow one. The heading takes the free space so the button stays hard right
 * rather than drifting in beside a short title.
 */
.blocal-section-head {
  align-items: center;
}

/* Centered variant: the measure is capped so the line length stays readable. */
.blocal-section-head.is-centered > p,
.blocal-section-head.is-centred > p {
  max-width: 44ch;
  margin-left: auto;
  margin-right: auto;
}

.blocal-section-head > :is(h1, h2, .wp-block-heading) {
  flex: 1 1 20ch;
  margin: 0;
}

@media (max-width: 640px) {
  .blocal-section-head {
    align-items: flex-start;
  }
}


/* -------------------------------------------------------------------------
 * The date column
 * ---------------------------------------------------------------------- */

/*
 * Stacked month over day, in amber, holding a fixed column to the left of the
 * details. Amber on this blue is 4.49:1, which clears AA for text at this size
 * with room to spare, and the same date is repeated in full underneath the
 * title, so nothing depends on reading it.
 *
 * Fixed width rather than intrinsic: an even left edge down the list matters
 * more than fitting "OCT" and "SEPT" to their own widths.
 */
.blocal-event-row .blocal-bigdate {
  /*
   * Sized to the widest thing it holds, which is a two-digit day, plus a little.
   * At 5.5rem the column carried about 26px of dead space to the right of every
   * date, which read as a gap between the date and the title on top of the
   * actual gap.
   */
  flex: 0 0 4.5rem;
  width: 4.5rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  margin: 0;
  color: var(--wp--preset--color--accent);
  line-height: 0.92;
}

.blocal-event-row .blocal-bigdate__month {
  font-size: 0.5em;
  letter-spacing: 0.02em;
}

/*
 * The chip sits above the title on a past row. Without the Upcoming chip the
 * two row types no longer share a first element, so the spacing is set here
 * rather than left to whichever block happens to come first.
 */
.blocal-event-row__body > .wp-block-blocal-event-status {
  margin: 0 0 0.35rem;
}

.blocal-event-row__body > .wp-block-post-title {
  margin-top: 0;
}

@media (max-width: 600px) {
  .blocal-event-row .blocal-bigdate {
    flex-basis: 4.25rem;
    width: 4.25rem;
  }
}

/* -------------------------------------------------------------------------
 * Arrows
 * ---------------------------------------------------------------------- */

/*
 * A trailing arrow on the links that are going somewhere, and on every button.
 *
 * The arrow is decorative: it repeats what the link text already says. Written
 * twice so that browsers supporting the alt-text syntax hide it from assistive
 * technology and older ones still draw it, rather than dropping it entirely
 * when they fail to parse the newer form.
 */
.blocal-arrow-link a::after,
.wp-block-button__link::after {
  content: "\2192";
  content: "\2192" / "";
  display: inline-block;
  margin-left: 0.45em;
  transition: transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.blocal-arrow-link a:hover::after,
.blocal-arrow-link a:focus-visible::after,
.wp-block-button__link:hover::after,
.wp-block-button__link:focus-visible::after {
  transform: translateX(0.28em);
}

/*
 * Standalone call-to-action links read a step up from body copy, since they are
 * the next thing to do rather than part of the sentence.
 *
 * The drawn underline stops short of the arrow. Left at 100% the stroke ran
 * under the arrowhead too, which made the two read as one smudged glyph.
 */
.blocal-arrow-link a {
  font-size: 1.125rem;
  background-size: calc(100% - 1.3em) 0.34em;
}

.blocal-arrow-link a:hover,
.blocal-arrow-link a:focus-visible {
  background-size: calc(100% - 1.3em) 0.42em;
}

/*
 * Nothing slides for anyone who asked for less movement. The arrow still
 * appears, because it is an affordance rather than an animation.
 */
@media (prefers-reduced-motion: reduce) {
  .blocal-arrow-link a::after,
  .wp-block-button__link::after {
    transition: none;
  }

  .blocal-arrow-link a:hover::after,
  .wp-block-button__link:hover::after {
    transform: none;
  }
}

/* -------------------------------------------------------------------------
 * Coneflower, bleeding from the left
 * ---------------------------------------------------------------------- */

/*
 * Mirrors the rhododendron on the primer band. Alternating the side stops the
 * two bleeds reading as one repeated device.
 *
 * Full strength, as exported.
 *
 * On a wide screen the copy does not actually cross it: at 1280 the paragraph's
 * leftmost glyph sits at 401 and the flower's ink ends at 375. Only the heading
 * overlaps, and at 56px that is large text, which needs 3:1 — white against the
 * flower's lightest pixel measures 3.54:1, so it passes.
 *
 * Modeling the text block's bounds suggested the paragraph would cross the
 * drawing below 1200, but the paragraph is capped at 44ch and centered, so its
 * actual glyphs clear the ink at every width down to the 782px breakpoint where
 * every bloom is hidden anyway. Verified in the browser rather than on paper.
 */
.blocal-bloom.is-coneflower::after {
  --bloom-w: min(32rem, 42vw);

  right: auto;
  left: 0;

  background-image: url("assets/img/purple-coneflower.png");
  background-image: image-set(
    url("assets/img/purple-coneflower.webp") type("image/webp"),
    url("assets/img/purple-coneflower.png") type("image/png")
  );

  /*
   * Pulled toward the edge. This file carries 16.7% of empty space down each
   * side, so anchored flush left the drawing would float a sixth of its own
   * width inside the band instead of running off it.
   */
  background-position: left calc(var(--bloom-w) * -0.1) bottom;
}

/*
 * The centered copy is held to a measure that keeps its paragraph clear of the
 * drawing. This is what makes full opacity safe above 1200.
 */
@media (min-width: 1200px) {
  .blocal-bloom.is-coneflower .blocal-section-head {
    margin-left: auto;
    margin-right: auto;
    max-width: 46rem;
  }
}


/* -------------------------------------------------------------------------
 * Two ways in
 * ---------------------------------------------------------------------- */

/*
 * A rule between the two columns, so they read as two choices rather than one
 * paragraph that happens to wrap into a second column.
 *
 * Drawn as a pseudo-element on the wrapper rather than as a border on the
 * second column. A border sits on that column's own edge, which puts it hard
 * against the second heading instead of centered in the gap; correcting for that
 * with negative margins would also eat into the column's width, since the
 * columns are sized border-box and the padding would come out of the 50%.
 */
.blocal-pathways {
  position: relative;
}

@media (min-width: 782px) {
  .blocal-pathways::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--wp--preset--color--rule);
  }
}

/*
 * Stacked, the same separation has to be horizontal. The columns block supplies
 * its own vertical gap, so this only adds the line and the space to clear it.
 */
@media (max-width: 781px) {
  .blocal-pathways > .wp-block-column + .wp-block-column {
    border-top: 1px solid var(--wp--preset--color--rule);
    padding-top: var(--wp--preset--spacing--50);
  }
}

/* -------------------------------------------------------------------------
 * Newsletter band
 * ---------------------------------------------------------------------- */

/*
 * Violet, and the copy is held to the left half.
 *
 * That split is a contrast requirement, not a layout preference. Violet is the
 * palette's weakest ground at 5.20:1 against white, and compositing the
 * columbine over it drops the lightest pixel to 2.84:1 — under the 3:1 that even
 * large text needs, let alone the 4.5:1 for body copy. Reducing the artwork's
 * opacity does not rescue it: at 0.65 it still only reaches 3.48:1.
 *
 * So nothing is set over the drawing. The copy and the form occupy the left, the
 * columbine occupies the right, and the two never meet.
 */
/*
 * Copy and form share the left, the columbine has the right.
 *
 * The split is a contrast requirement, not a preference. Violet is the weakest
 * ground in the palette at 5.20:1 against white, and compositing the columbine
 * over it drops the lightest pixel to 2.84:1 — under the 3:1 that even large
 * text needs. Dimming does not rescue it: 0.65 opacity still only reaches
 * 3.48:1. So nothing is set over the drawing.
 *
 * !important because core's constrained layout centers every child with
 * `margin-inline: auto`, and nothing is more specific than a rule that already
 * carries it. Without it the block centers and runs into the artwork.
 */
/*
 * The wrapper is alignwide, so it takes the same 1200 column as every other
 * band and its left edge lines up with the page. What gets pinned and capped is
 * the columns block inside it — pinning the wrapper itself would have anchored
 * the copy to the band's padding edge, roughly 43px in, which is outside the
 * grid the rest of the site sits on.
 */
/*
 * Title and copy on the left, form on the right, and the form level with the
 * top of the paragraph rather than the top of the title.
 *
 * A two-row grid rather than two columns. In a columns layout the form starts
 * at the top of its own column, which is level with the title; the only way to
 * drop it to the paragraph is to push it down by the title's height, and that
 * height changes every time the title wraps. Giving the title its own row means
 * the paragraph and the form share the next one and stay level by construction.
 */
.blocal-newsletter__grid {
  display: grid;
  /*
   * minmax(0, ...) rather than a bare 1fr. A default fr track will not shrink
   * below its content's min-content width, and this cell holds a flex row whose
   * min-content is the button's 229px plus an input's intrinsic size — about
   * 414px. Between 782 and 899 the track was handed roughly 323px, refused it,
   * and pushed the whole document 44px wider than the viewport, so every page on
   * the site scrolled sideways at tablet widths. The stacking query below is what
   * actually fixes those widths; this is the guard that stops any future content
   * in this cell doing the same thing.
   */
  grid-template-columns: 42% minmax(0, 1fr);
  column-gap: var(--wp--preset--spacing--60);
  row-gap: var(--wp--preset--spacing--40);
  align-items: start;
  max-width: none !important;
  margin-left: 0 !important;
  margin-right: auto !important;
}

.blocal-newsletter__title  { grid-area: 1 / 1 / 2 / 2; margin: 0; }
.blocal-newsletter__intro  { grid-area: 2 / 1 / 3 / 2; margin: 0; }
.blocal-newsletter__form   { grid-area: 2 / 2 / 3 / 3; margin: 0; }

/*
 * Stacked, in reading order, once there is no room for two columns.
 *
 * 899, not 781. The form needs its email box and its button on one line, which
 * takes about 380px; at 782 the second track has around 323px to give. Stacking
 * from 899 down hands the form the band's full width instead of a cell it does
 * not fit in, and 900 is the first width where the two-column form has room to
 * look deliberate.
 */
@media (max-width: 899px) {
  .blocal-newsletter__grid {
    grid-template-columns: 1fr;
  }

  .blocal-newsletter__title { grid-area: auto; }
  .blocal-newsletter__intro { grid-area: auto; }
  .blocal-newsletter__form  { grid-area: auto; }
}

/*
 * One line: the field takes the room that is left and the button sits beside it.
 *
 * Gravity stacks the footer under the fields, so the form itself becomes the
 * flex row. The field container grows, the footer does not, and min-width:0 lets
 * the input actually shrink inside it rather than forcing the row wider than the
 * column.
 */
.blocal-newsletter .gform_wrapper {
  max-width: 34rem;
}

/*
 * Inline only while the form is a single field.
 *
 * The row exists so an email box and its button sit on one line. The moment the
 * form gains a second field the body becomes two rows tall, the footer stays one
 * row, and the button ends up stranded level with the top of the first field.
 * :has() lets the layout decide for itself rather than depending on whoever
 * edits the form in the admin remembering that a stylesheet made an assumption
 * about it.
 */
.blocal-newsletter .gform_wrapper form:not(:has(.gform_fields > .gfield:nth-child(2))) {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
}

/*
 * Two or more fields: the form stacks, the button sits under it, and the field
 * grid gets the full width of the column rather than the single-field cap.
 */
.blocal-newsletter .gform_wrapper:has(.gform_fields > .gfield:nth-child(2)) {
  max-width: 30rem;
}

.blocal-newsletter .gform_wrapper form:has(.gform_fields > .gfield:nth-child(2)) .gform_footer,
.blocal-newsletter .gform_wrapper form:has(.gform_fields > .gfield:nth-child(2)) .gform-footer {
  margin-top: 1rem;
}

.blocal-newsletter .gform_wrapper .gform_body,
.blocal-newsletter .gform_wrapper .gform-body {
  flex: 1 1 auto;
  min-width: 0;
}

.blocal-newsletter .gform_wrapper .gform_footer,
.blocal-newsletter .gform_wrapper .gform-footer {
  flex: 0 0 auto;
  margin: 0;
  padding: 0;
}

.blocal-newsletter .gform_wrapper .gform_button {
  white-space: nowrap;
}

/*
 * The field fills its container, stated rather than assumed.
 *
 * What is established: the newsletter field is 306px wide on every page that
 * carries only this form, and 145px on /grants/, which is the one page with a
 * second Gravity form on it. Same markup, same classes, same four Gravity
 * stylesheets — /grants/ simply loads one more, and the field shrinks to
 * roughly half while the button stays put.
 *
 * What is NOT established: which rule in that extra sheet does it. There is no
 * `.medium` width rule in any stylesheet or inline block on the page, and a
 * walk of every accessible CSSOM rule that sets width or max-width matched
 * nothing against this input. So the !important here answers a rule this
 * comment cannot name, which is exactly the kind of !important the rest of this
 * file argues against — and it is still the right call, because the alternative
 * is a site-wide part whose layout depends on which page it lands on.
 *
 * If it is ever identified, replace this with specificity and say what it was.
 */
.blocal-newsletter .gform_wrapper .ginput_container input {
  width: 100% !important;

  /* An input carries an intrinsic width that width:100% does not remove; without
   * this it cannot shrink inside the flex row and widens the row instead. */
  min-width: 0;
}

/*
 * The "indicates required fields" legend is noise here: there is one field, its
 * label is already hidden, and the input still carries aria-required for anyone
 * on a screen reader.
 */
.blocal-newsletter .gform_wrapper .gform_required_legend {
  display: none;
}

/*
 * Stacked on narrow screens, where an inline field and button will not fit.
 *
 * The selector has to carry the same :not(:has()) as the rule it is undoing.
 * That rule is far more specific than a bare `form`, so a plain override here
 * lost silently and the form stayed a flex row all the way down: at 375 the
 * button kept its 229px, the field took what was left, and an email input
 * rendered 97px wide. Specificity, not the media query, was the thing that was
 * wrong — the query had been matching correctly and doing nothing.
 */
@media (max-width: 600px) {
  .blocal-newsletter .gform_wrapper form:not(:has(.gform_fields > .gfield:nth-child(2))) {
    display: block;
  }

  .blocal-newsletter .gform_wrapper .gform_footer,
  .blocal-newsletter .gform_wrapper .gform-footer {
    margin-top: 0.75rem;
  }
}

/*
 * This one climbs instead of dipping.
 *
 * The other two bleed past the bottom of their bands. Doing that here would run
 * the drawing into the footer and sit it over that section's text, so it breaks
 * the top edge instead — the same gesture inverted, which keeps the device
 * consistent without repeating it exactly.
 *
 * The box is pinned to the band's bottom, so the drawing is cropped there and
 * only the top escapes.
 */
.blocal-bloom.is-columbine::after,
.blocal-newsletter.is-columbine::after {
  content: "";
  position: absolute;
  z-index: -1;
  pointer-events: none;
  --bloom-w: min(30rem, 40vw);
  top: calc(var(--bloom-w) * -0.14);
  right: 0;
  bottom: 0;
  left: auto;
  width: var(--bloom-w);

  background-image: url("assets/img/wild-columbine.png");
  background-image: image-set(
    url("assets/img/wild-columbine.webp") type("image/webp"),
    url("assets/img/wild-columbine.png") type("image/png")
  );
  background-repeat: no-repeat;
  background-position: right top;
  background-size: var(--bloom-w) auto;
}

.blocal-newsletter {
  position: relative;
  z-index: 1;
}

@media (max-width: 781px) {
  .blocal-newsletter.is-columbine::after {
    display: none;
  }
}

/* -------------------------------------------------------------------------
 * Gravity Forms
 * ---------------------------------------------------------------------- */

/*
 * Site-wide, not per-band. Forms appear on the newsletter module, event
 * registration and anywhere else a form is dropped in, and every one of them
 * should arrive already looking like this site.
 *
 * Gravity's Orbital theme is built to be themed through custom properties
 * rather than by overriding its selectors, so this sets the properties. The
 * names are taken from the plugin's own theme-framework stylesheet rather than
 * guessed.
 *
 * Defaults here are for a light ground; the dark-band block below reverses only
 * what has to reverse.
 */
.gform_wrapper.gform-theme {
  /* controls */
  --gf-ctrl-bg-color: var(--wp--preset--color--base);
  --gf-ctrl-bg-color-hover: var(--wp--preset--color--base);
  --gf-ctrl-bg-color-focus: var(--wp--preset--color--base);
  --gf-ctrl-color: var(--wp--preset--color--contrast);
  --gf-ctrl-border-color: var(--wp--preset--color--contrast);
  --gf-ctrl-border-color-hover: var(--wp--preset--color--contrast);
  --gf-ctrl-border-color-focus: var(--wp--preset--color--accent);
  --gf-ctrl-border-width: 1px;
  --gf-ctrl-accent-color: var(--wp--preset--color--accent);

  /* primary button, matching the site's own */
  --gf-ctrl-btn-bg-color-primary: var(--wp--preset--color--accent);
  --gf-ctrl-btn-bg-color-hover-primary: var(--wp--preset--color--contrast);
  --gf-ctrl-btn-bg-color-focus-primary: var(--wp--preset--color--contrast);
  --gf-ctrl-btn-color-primary: var(--wp--preset--color--contrast);
  --gf-ctrl-btn-color-hover-primary: var(--wp--preset--color--accent);
  --gf-ctrl-btn-color-focus-primary: var(--wp--preset--color--accent);
  --gf-ctrl-btn-border-color-primary: var(--wp--preset--color--contrast);
  --gf-ctrl-btn-border-color-hover-primary: var(--wp--preset--color--contrast);
  --gf-ctrl-btn-border-color-focus-primary: var(--wp--preset--color--contrast);

  /*
   * Size, through Gravity's own scale rather than by overriding its padding.
   *
   * --gf-ctrl-size-md is 38px out of the box, which is what made both the field
   * and the submit 38px tall against the site's 48px buttons. Setting the
   * variable moves both together and keeps the plugin doing its own maths.
   */
  --gf-ctrl-size: 48px;
  --gf-ctrl-size-md: 48px;
  --gf-ctrl-btn-size: 48px;
  --gf-ctrl-btn-size-md: 48px;
  --gf-ctrl-btn-padding-x: 24px;
  --gf-ctrl-btn-padding-x-md: 24px;
  --gf-ctrl-btn-font-size: 1rem;
  --gf-ctrl-btn-font-size-md: 1rem;
  --gf-ctrl-btn-line-height: 1.2;
  --gf-ctrl-btn-radius: 5px;
  --gf-ctrl-font-size: 1rem;
  --gf-ctrl-padding-x: 16px;
  --gf-ctrl-radius: 5px;
}

/*
 * Form furniture follows the site's other furniture onto the sans. Labels,
 * inputs and buttons are UI, not reading matter, and the body serif goes weak
 * at control sizes.
 */
.gform_wrapper.gform-theme .gfield_label,
.gform_wrapper.gform-theme legend,
.gform_wrapper.gform-theme input,
.gform_wrapper.gform-theme select,
.gform_wrapper.gform-theme textarea,
.gform_wrapper.gform-theme .gform_button,
.gform_wrapper.gform-theme button {
  font-family: var(--wp--preset--font-family--display);
}

.gform_wrapper.gform-theme .gfield_label,
.gform_wrapper.gform-theme legend {
  font-weight: 700;
  letter-spacing: -0.005em;
}

/*
 * The button, matched to the site's. The properties above color it; these set
 * the shape, which Orbital sizes on its own scale.
 */
.gform_wrapper.gform-theme .gform_button,
.gform_wrapper.gform-theme button[type="submit"],
.gform_wrapper.gform-theme input[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45em;
  border-width: 1px;
  border-style: solid;
  font-weight: 700;
  letter-spacing: -0.01em;

  /*
   * !important on the font because Orbital resets every descendant with
   * `font-family: unset` inside a :where() block. That reset makes the button
   * inherit, and what it inherits on a page whose body is a serif is the serif,
   * which is how the submit label ended up in Libre Baskerville while the input
   * beside it was correctly in Inter.
   */
  font-family: var(--wp--preset--font-family--display) !important;

  transition: background-color 140ms ease-out, color 140ms ease-out,
              border-color 140ms ease-out, transform 140ms ease-out;
}

/* The same trailing arrow every other call to action on the site carries. */
/*
 * The arrow needs !important, and specifically because of how Orbital blanks
 * it. Gravity ships
 *
 *   .gform-theme.gform-theme--framework.gform_wrapper .button::after
 *     { content: var(--gf-local-content) }
 *
 * which is four classes deep and therefore outranks anything sane, and that
 * variable is unset — so the declaration is invalid at computed-value time and
 * the pseudo-element renders nothing at all. The submit carries both
 * `gform_button` and `button`, so it matches.
 *
 * Declared twice as everywhere else: the second form hides the arrow from
 * assistive technology, the first keeps it drawn in browsers that cannot parse
 * the alt-text syntax.
 */
.gform_wrapper.gform-theme .gform_button::after,
.gform_wrapper.gform-theme button[type="submit"]::after {
  content: "\2192" !important;
  content: "\2192" / "" !important;
  display: inline-block;
  transition: transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1);

  /*
   * The arrow follows the label's color, which it did not.
   *
   * On the newsletter band the button is white-on-green and the arrow rendered
   * black. `color` was not the culprit — Orbital sets -webkit-text-fill-color
   * on the pseudo-element, and that beats `color` for text rendering wherever
   * it is set, so the inherited white never reached the glyph.
   *
   * Both are reset here so the arrow tracks the label on every ground rather
   * than being corrected band by band.
   *
   * !important for the same reason the content declaration above needs it:
   * Orbital's rule is `.gform-theme.gform-theme--framework.gform_wrapper
   * .button::after`, four classes, and nothing sane written against
   * `.gform_wrapper.gform-theme .gform_button::after` outranks it. Measured
   * without: the arrow stayed black on the newsletter and Orbital's own
   * rgba(17,35,55,.65) on the grant form.
   */
  color: inherit !important;
  -webkit-text-fill-color: currentColor !important;
}

.gform_wrapper.gform-theme .gform_button:hover::after,
.gform_wrapper.gform-theme button[type="submit"]:hover::after,
.gform_wrapper.gform-theme .gform_button:focus-visible::after,
.gform_wrapper.gform-theme button[type="submit"]:focus-visible::after {
  transform: translateX(0.28em);
}

.gform_wrapper.gform-theme .gform_button:hover,
.gform_wrapper.gform-theme button[type="submit"]:hover,
.gform_wrapper.gform-theme input[type="submit"]:hover {
  transform: translateY(-1px);
}

/*
 * Fields fill their container. Gravity sizes inputs on its own scale, which
 * left a 200px email box inside a 416px form; letting them fill means the
 * container is what decides the width, and that is capped where it matters.
 */
.gform_wrapper.gform-theme input[type="text"],
.gform_wrapper.gform-theme input[type="email"],
.gform_wrapper.gform-theme input[type="tel"],
.gform_wrapper.gform-theme input[type="number"],
.gform_wrapper.gform-theme select,
.gform_wrapper.gform-theme textarea {
  width: 100%;
  padding: 0.8rem 1rem;
  border-radius: 5px;
  font-size: 1rem;
}

/*
 * On a colored band the labels and helper text have to reverse, and the field
 * borders would otherwise be black on a dark ground. The controls stay white
 * with black text: a white field on a colored band is the clearest signal that
 * it is a place to type.
 */
.has-surface-violet-background-color .gform_wrapper.gform-theme,
.has-surface-blue-background-color .gform_wrapper.gform-theme,
.has-surface-inverse-background-color .gform_wrapper.gform-theme,
.wp-block-cover .gform_wrapper.gform-theme {
  --gf-ctrl-border-color: var(--wp--preset--color--base);
  --gf-ctrl-border-color-hover: var(--wp--preset--color--base);
  --gf-local-color: var(--wp--preset--color--base);
}

.has-surface-violet-background-color .gform_wrapper.gform-theme :is(.gfield_label, legend, .gfield_description, .gform_description, .gform_required_legend),
.has-surface-blue-background-color .gform_wrapper.gform-theme :is(.gfield_label, legend, .gfield_description, .gform_description, .gform_required_legend),
.has-surface-inverse-background-color .gform_wrapper.gform-theme :is(.gfield_label, legend, .gfield_description, .gform_description, .gform_required_legend),
.wp-block-cover .gform_wrapper.gform-theme :is(.gfield_label, legend, .gfield_description, .gform_description, .gform_required_legend) {
  color: var(--wp--preset--color--base);
}

/*
 * Backstop. Orbital's own button rules are specific enough to beat anything a
 * theme can reasonably write, so the colors are forced here as well as set
 * through the properties above. Deliberate, and limited to three declarations.
 */
.gform_wrapper.gform-theme .gform_button,
.gform_wrapper.gform-theme button[type="submit"],
.gform_wrapper.gform-theme input[type="submit"] {
  background-color: var(--wp--preset--color--accent) !important;
  border-color: var(--wp--preset--color--contrast) !important;
  color: var(--wp--preset--color--contrast) !important;
}

.gform_wrapper.gform-theme .gform_button:hover,
.gform_wrapper.gform-theme button[type="submit"]:hover,
.gform_wrapper.gform-theme input[type="submit"]:hover,
.gform_wrapper.gform-theme .gform_button:focus-visible,
.gform_wrapper.gform-theme button[type="submit"]:focus-visible {
  background-color: var(--wp--preset--color--contrast) !important;
  color: var(--wp--preset--color--accent) !important;
}

@media (prefers-reduced-motion: reduce) {
  .gform_wrapper.gform-theme .gform_button:hover,
  .gform_wrapper.gform-theme button[type="submit"]:hover {
    transform: none;
  }
}

/*
 * The newsletter heading is smaller than a section heading.
 *
 * At the band scale it was 55px inside a 237px column and broke over four
 * lines. It is also a utility module that now repeats on every page, and
 * something a visitor sees seven times should not shout as loudly as the
 * heading of the section they came for.
 */
.blocal-newsletter h2.has-x-large-font-size {
  font-size: clamp(1.75rem, 2.6vw, 2.5rem) !important;
  line-height: 1.05;
}

/*
 * On a colored band the submit button is the outline style, matching "Give
 * Now" in the footer: transparent, a white rule, white label.
 *
 * A solid amber block on violet fights the band; the outline sits in it. On a
 * light ground the solid amber stays, which is the same rule the rest of the
 * site's buttons follow.
 */
.has-surface-violet-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]),
.has-surface-blue-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]),
.wp-block-cover .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]) {
  background-color: var(--wp--preset--color--surface-inverse) !important;

  /*
   * No rule. This carried a half-white hairline, on the reasoning that green
   * against violet is only 1.92:1 and the fill alone leaves the button's edge
   * indistinct. That is still true and the edge is softer without it — removed
   * on request 2026-08-30, weighed against a label that is white on green at
   * 9.11:1 and was never the thing at risk.
   */
  border-color: transparent !important;
  color: var(--wp--preset--color--base) !important;
}

/*
 * Filled, not outlined, because this button sits over the illustration. A
 * transparent button on a painting has the drawing running through its label;
 * a solid one gives it a ground of its own. White on this green is 9.11:1.
 *
 * The white rule is gone as of 2026-08-30. Green against violet is 1.92:1, so
 * the edge is softer for it; the label is white on green at 9.11:1 and was
 * never the part at risk.
 */

/* On the green band itself a green fill would vanish, so that one keeps amber. */
.has-surface-inverse-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]) {
  background-color: var(--wp--preset--color--accent) !important;
  border-color: var(--wp--preset--color--contrast) !important;
  color: var(--wp--preset--color--contrast) !important;
}

/*
 * Filling with amber rather than with black: on a dark band a black fill would
 * be a hole rather than a button. Same move the theme's own outline buttons
 * make on these grounds.
 */
.has-surface-violet-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]):hover,
.has-surface-blue-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]):hover,
.has-surface-inverse-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]):hover,
.wp-block-cover .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"], input[type="submit"]):hover,
.has-surface-violet-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"]):focus-visible,
.has-surface-blue-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"]):focus-visible,
.has-surface-inverse-background-color .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"]):focus-visible,
.wp-block-cover .gform_wrapper.gform-theme :is(.gform_button, button[type="submit"]):focus-visible {
  background-color: var(--wp--preset--color--accent) !important;
  border-color: var(--wp--preset--color--accent) !important;
  color: var(--wp--preset--color--contrast) !important;
}

/*
 * Sub-labels — the "First" and "Last" under a name field — reverse on a
 * colored band along with everything else. Gravity styles them separately from
 * the main label, so they need naming separately too.
 */
.has-surface-violet-background-color .gform_wrapper.gform-theme .gform-field-label--type-sub,
.has-surface-blue-background-color .gform_wrapper.gform-theme .gform-field-label--type-sub,
.has-surface-inverse-background-color .gform_wrapper.gform-theme .gform-field-label--type-sub,
.wp-block-cover .gform_wrapper.gform-theme .gform-field-label--type-sub {
  color: rgba(255, 255, 255, 0.86);
}

/* -------------------------------------------------------------------------
 * Footer
 * ---------------------------------------------------------------------- */

.blocal-footer-logo {
  margin: 0;
}

.blocal-footer-logo img {
  display: block;
  height: auto;
}

/*
 * The nav list drops its bullets and its indent so it starts on the column's
 * own left edge, in line with everything above it.
 */
.blocal-footer-nav {
  list-style: none;
  margin: 0;
  padding-left: 0;
}

.blocal-footer-nav li {
  margin-bottom: 0.45rem;
}

/*
 * One link, icon and words together, rather than a bare glyph a visitor has to
 * recognize. The icon is decorative — the link already says where it goes — so
 * it is hidden from assistive technology and the text carries the label.
 */
.blocal-footer-social {
  margin: 0;
  font-size: 0.8125rem;
}

.blocal-footer-social a {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.blocal-footer-social__icon {
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
}

/*
 * Footer links are white rather than the softened white the body text uses.
 * On this blue that is 8.55:1, and it separates what is clickable from what is
 * merely written without relying on an underline in a column of six of them.
 */
footer a {
  color: var(--wp--preset--color--base);
}

/* =========================================================================
 * LONG-FORM PAGE MODULES
 *
 * Built for /become-a-b-corp/, which is the longest page on the site by some
 * distance and the first one whose job is to be read rather than scanned.
 *
 * Everything below is generic. None of it names that page, because the same
 * problems arrive on /about/, /join/ and /grant/: a list where each item leads
 * with a label, a figure that has to announce its own age, a quotation, and a
 * contents list for a page too long to scroll blind.
 * ====================================================================== */

/* -------------------------------------------------------------------------
 * Rule color on a dark band
 * ---------------------------------------------------------------------- */

/*
 * The `rule` token is black at 16%, which is a hairline on a light ground and
 * nothing at all on a dark one. Every module below separates items with a
 * hairline, so each needs both values.
 *
 * Declared once as a custom property that the dark bands re-point, rather than
 * duplicating every border rule per background class.
 */
:root {
  --blocal-hairline: var(--wp--preset--color--rule);
}

.has-surface-inverse-background-color,
.has-surface-blue-background-color,
.has-surface-violet-background-color,
.wp-block-cover {
  --blocal-hairline: rgba(255, 255, 255, 0.24);
}

/* -------------------------------------------------------------------------
 * Points: a list whose items lead with a label
 * ---------------------------------------------------------------------- */

/*
 * The page's most repeated structure. Every item is a bold lead-in sentence
 * followed by the explanation: "You have been in business less than a year.
 * B Lab requires 12 months of operating history."
 *
 * Set as a plain <ul> so the markup stays a list for assistive technology and
 * for anyone editing it in wp-admin, where a definition list is not reachable
 * from the block inserter at all.
 *
 * No bullet glyph. The lead-in is the marker: it is set in the display sans
 * against the serif that follows, which separates label from explanation more
 * clearly than a disc in the margin ever did, and it survives being read aloud.
 */
.blocal-points {
  list-style: none;
  margin: var(--wp--preset--spacing--40) 0 0;
  padding: 0;
  max-width: 44rem;
}

/*
 * A hairline between items rather than around them. Nine disqualifiers in a
 * row need a rhythm to be scannable, and boxing each one would build a grid of
 * cards out of what is really a single argument.
 */
.blocal-points > li {
  padding: var(--wp--preset--spacing--40) 0;
  border-top: 1px solid var(--blocal-hairline);
}

.blocal-points > li:last-child {
  border-bottom: 1px solid var(--blocal-hairline);
}

/*
 * The lead-in takes the display face. Left in the body serif it reads as mere
 * emphasis inside the sentence; in Inter it reads as the thing the item is
 * about, which is what it is.
 */
.blocal-points > li > strong:first-child {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 700;
  letter-spacing: -0.02em;
  /* Slightly up on the serif beside it: Inter sets small for its point size. */
  font-size: 1.0625em;
}

/*
 * There is deliberately no rule here for a label-only item.
 *
 * The obvious one — `strong:only-child { display: block }` — matched every
 * lead-in on the page. :only-child counts element children and ignores text
 * nodes, so in `<li><strong>Label.</strong> Explanation</li>` the strong IS the
 * only child, and every item in every list broke its label onto its own line.
 * CSS cannot tell the two cases apart; a label-only item would need a class.
 */

/* -------------------------------------------------------------------------
 * Topics: the same list, gridded
 * ---------------------------------------------------------------------- */

/*
 * Seven items stacked is a wall. The seven Impact Topics are a set to be taken
 * in at a glance and referred back to, not an argument read top to bottom, so
 * they go two-up and the reader can see the whole shape of the standard at once.
 *
 * Two columns rather than three: each item carries a full sentence, and at
 * three across the measure drops under 30 characters and every item wraps to
 * five lines.
 */
@media (min-width: 900px) {
  .blocal-points.is-topics {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--wp--preset--spacing--60);
    /*
     * Wider than the 44rem a single column of prose gets, but not the full
     * 1200: spanning the band put each column at 560px, which is about 78
     * characters and longer than anything else on the page. This lands them
     * near 28rem apiece.
     */
    max-width: 62rem;
  }

  /*
   * In a grid the top border no longer draws a continuous line down a single
   * column, so the last-child bottom border would land under item seven only
   * and leave item six open. Every item closes itself instead.
   */
  .blocal-points.is-topics > li {
    border-top: 0;
    border-bottom: 1px solid var(--blocal-hairline);
  }
}

/* -------------------------------------------------------------------------
 * Figure: a number that states its own age
 * ---------------------------------------------------------------------- */

/*
 * Every figure on this page is dated in the text, so a stale number announces
 * itself rather than sitting there looking current. This gives that rule a
 * shape: the amount, what it buys, and when it was last true.
 *
 * The amber rule is decorative and carries no text, so the accent's 1.91:1
 * against white is not a contrast question here. It is also the palette's
 * stated use — "small elements" — rather than a background.
 */
.blocal-figure {
  margin: var(--wp--preset--spacing--40) 0;
  padding: var(--wp--preset--spacing--30) 0 var(--wp--preset--spacing--30)
    var(--wp--preset--spacing--40);
  border-left: 4px solid var(--wp--preset--color--accent);
  max-width: 34rem;
}

.blocal-figure__amount {
  display: block;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 800;
  font-size: clamp(2.25rem, 5vw, 3rem);
  line-height: 1;
  letter-spacing: -0.04em;
  margin: 0;
}

.blocal-figure__what {
  display: block;
  margin: 0.6rem 0 0;
  max-width: 28rem;
}

/*
 * The date stamp, deliberately quiet. It is maintenance metadata that the
 * reader should be able to find and then ignore.
 *
 * `muted` is black at 62%, which is 6.4:1 on white. It is not used on the dark
 * bands: softened white on violet measures 3.89 and fails, so this module
 * belongs on a light ground and the stamp inherits full color if it is ever
 * moved.
 */
.blocal-figure__asof {
  display: block;
  margin: 0.7rem 0 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.5;
  color: var(--wp--preset--color--muted);
}

/* -------------------------------------------------------------------------
 * Quote: somebody else's words
 * ---------------------------------------------------------------------- */

/*
 * Used for B Lab's own wording on the North Carolina statute, where quoting
 * verbatim is the point: the claim is contentious enough that paraphrasing it
 * would invite the reader to doubt it.
 *
 * A `rule` hairline rather than the figure's amber bar, so the two modules do
 * not read as the same component with different contents. Italic serif, which
 * is the one place the italic face earns its 36KB.
 */
.blocal-quote {
  margin: var(--wp--preset--spacing--40) 0;
  padding-left: var(--wp--preset--spacing--40);
  border-left: 2px solid var(--blocal-hairline);
  max-width: 36rem;
}

.blocal-quote p {
  font-style: italic;
  font-size: 1.25rem;
  line-height: 1.6;
  margin: 0;
}

.blocal-quote cite {
  display: block;
  margin-top: 0.85rem;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  font-style: normal;
  letter-spacing: 0.02em;
}

/* -------------------------------------------------------------------------
 * Cycle: the five-year shape
 * ---------------------------------------------------------------------- */

/*
 * "You meet the Year 0 requirements to certify, a further set at Year 3 with an
 * audit attached, and you recertify at Year 5" is one sentence describing a
 * commitment most readers assume is a single pass. Drawn as three stops it
 * answers the question the sentence raises — how long am I signed up for — in
 * the time it takes to look at it.
 *
 * A panel rather than three bare columns, because it sits mid-band inside the
 * page's longest stretch of running prose and needs an edge to be read as an
 * aside rather than as the next paragraph.
 */
.blocal-cycle {
  margin: var(--wp--preset--spacing--50) 0;
  border: 1px solid var(--blocal-hairline);
  border-radius: 6px;
  display: grid;
  gap: 0;
  max-width: 44rem;
}

@media (min-width: 700px) {
  .blocal-cycle {
    grid-template-columns: repeat(3, 1fr);
  }
}

.blocal-cycle > .blocal-cycle__stop {
  padding: var(--wp--preset--spacing--40);

  /*
   * Core gives every child of a flow layout a top margin of one block-gap and
   * exempts the first. Turning the container into a grid does not undo that, so
   * stops two and three were sitting 1.4rem lower than stop one and the panel
   * read as a mistake rather than as three stages.
   *
   * Two classes deep because core's rule scores one — `:root :where(.is-layout-
   * flow) > *`, where :where() contributes nothing — and a tie would be settled
   * by whichever stylesheet the browser happened to load second.
   */
  margin-block-start: 0;
}

/*
 * The divider follows the axis the panel is currently stacked on: a vertical
 * rule between columns on a wide screen, a horizontal one between rows when
 * they stack. Written as two rules rather than one border that changes side,
 * because the first item must never carry either.
 */
@media (min-width: 700px) {
  .blocal-cycle__stop + .blocal-cycle__stop {
    border-left: 1px solid var(--blocal-hairline);
  }
}

@media (max-width: 699px) {
  .blocal-cycle__stop + .blocal-cycle__stop {
    border-top: 1px solid var(--blocal-hairline);
  }
}

.blocal-cycle__year {
  display: block;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 800;
  font-size: 1.25rem;
  letter-spacing: -0.03em;
  line-height: 1;
}

.blocal-cycle__what {
  display: block;
  margin-top: 0.55rem;
  font-size: 0.9375rem;
  line-height: 1.6;
}

/* -------------------------------------------------------------------------
 * Contents: a way into a page too long to scroll blind
 * ---------------------------------------------------------------------- */

/*
 * Nine headings, each written to match a different search query. A reader who
 * arrived on "b corp certification cost" wants the seventh of them, and without
 * this they scroll fourteen hundred words to find it.
 *
 * Three columns, filled down each column in turn rather than across the row, so
 * the reading order of the list matches the order of the page.
 */
.blocal-toc {
  list-style: none;
  margin: var(--wp--preset--spacing--30) 0 0;
  padding: 0;
  columns: 1;
  column-gap: var(--wp--preset--spacing--60);
}

@media (min-width: 700px) {
  .blocal-toc {
    columns: 2;
  }
}

@media (min-width: 1000px) {
  .blocal-toc {
    columns: 3;
  }
}

.blocal-toc > li {
  /* Keeps an item from being split across a column boundary. */
  break-inside: avoid;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--blocal-hairline);
}

/*
 * The drawn stroke is the site's link signal, but nine of them permanently
 * underlined is a thicket. These carry the stroke on hover only, the same
 * bargain the navigation makes for the same reason.
 *
 * The selector is deep enough to beat `.blocal-band p a`, which is a class plus
 * a type and would otherwise paint every one of them amber at rest.
 */
/*
 * One treatment, both contents components: the strip on the B Corp page and the
 * rail in an interior hero. They looked different, and the difference was
 * accidental — the rail pads its rows to 40px for a comfortable target, and
 * `background-position: 100%` resolves against the PADDED box, so the stroke
 * dropped with the padding and sat 11px under the words against the strip's 4.
 *
 * background-origin: content-box measures from the text instead, so the stroke
 * hugs the words no matter how much room the row has around them. That is what
 * makes one rule safe for two components with different padding, and it is why
 * neither of them states a padding-dependent offset any more.
 */
ul.blocal-toc a,
.blocal-contents .blocal-contents__list a {
  display: block;
  font-family: var(--wp--preset--font-family--display);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.4;
  text-decoration: none;
  background-image: none;
  background-repeat: no-repeat;
  background-origin: content-box;
  background-position: 0 calc(100% + 2px);
  background-size: 100% 0;
  transition: background-size 140ms ease-out;
}

/* The strip's rows are the text and little else, so they carry their own gap. */
ul.blocal-toc a {
  padding-bottom: 0.15em;
}

ul.blocal-toc a:hover,
ul.blocal-toc a:focus-visible,
.blocal-contents .blocal-contents__list a:hover,
.blocal-contents .blocal-contents__list a:focus-visible {
  background-image: var(--blocal-brush);
  background-size: 100% 0.3em;
}

/*
 * Anything jumped to lands clear of the sticky-free header rather than hard
 * against the viewport edge, and the whole page keeps a smooth jump for anyone
 * who has not asked for less movement.
 */
:target {
  scroll-margin-top: var(--wp--preset--spacing--50);
}

/*
 * Smoothed unconditionally. The reduced-motion block at the top of this file
 * already forces `scroll-behavior: auto !important` on everything, so repeating
 * the preference query here would be a second copy of a rule that is already
 * enforced globally.
 */
html {
  scroll-behavior: smooth;
}

/* -------------------------------------------------------------------------
 * Leaving the site
 * ---------------------------------------------------------------------- */

/*
 * Four links on this page go to B Lab, and every one of them is there because
 * the number behind it changes and should not be copied here. The reader is
 * better off knowing which links are the chapter's and which hand them over.
 *
 * A north-east arrow instead of the arrow-link's east one. Same component, one
 * glyph different: onward through our site, or out of it.
 *
 * Written twice for the same reason the original is — browsers that understand
 * the alt-text syntax hide it from assistive technology, older ones still draw
 * it — and the alternative text is empty because the destination is already in
 * the link.
 */
.blocal-arrow-link.is-external a::after {
  content: "\2197";
  content: "\2197" / "";
}

/*
 * The arrow no longer travels sideways, because this one is not pointing along
 * the reader's path. It lifts away instead.
 */
.blocal-arrow-link.is-external a:hover::after,
.blocal-arrow-link.is-external a:focus-visible::after {
  transform: translate(0.16em, -0.16em);
}

/*
 * The same signal on a link inside a sentence.
 *
 * Without this the convention is only half-applied: a standalone call to
 * action says whether it leaves the site and an inline link does not, which is
 * worse than not marking either, because the reader learns a rule that then
 * fails them.
 *
 * Smaller and lifted, because it sits mid-sentence and a full-size arrow at
 * the baseline would break the line's rhythm. Not sized in em of the parent —
 * these appear in the serif body at 1rem and in the sans callout at 0.9375rem,
 * and the mark should be the same weight in both.
 *
 * The class goes on the anchor here, where .blocal-arrow-link.is-external puts
 * it on the paragraph. That is not an inconsistency to tidy up: a standalone
 * CTA is one link that IS the paragraph, and an inline one is a link among
 * words, so the class lands on whichever element the whole thing is.
 */
a.is-external::after {
  content: "\2197";
  content: "\2197" / "";
  display: inline-block;
  font-size: 0.72em;
  vertical-align: 0.28em;
  margin-left: 0.14em;
  /* The brush stroke is drawn under the link's own box; the mark sits outside
     it so the stroke stops at the last letter rather than running under a
     floating arrow. */
  line-height: 1;
}

@media (prefers-reduced-motion: reduce) {
  .blocal-arrow-link.is-external a:hover::after {
    transform: none;
  }
}

/* -------------------------------------------------------------------------
 * Offers: three of them, side by side, on a dark band
 * ---------------------------------------------------------------------- */

/*
 * The chapter's three concrete offers — the grant, the sequenced resources, and
 * a person to talk to. Three columns rather than the two the homepage uses,
 * with a button under each, because these are not a fork in the road. They are
 * three separate things on offer and a reader may want any or all of them.
 *
 * Every button is the outline variant. Three amber fills across one band would
 * shout in three directions at once, and the page's single primary action lives
 * in the section below this one, where it can be the only button on the band.
 */
.blocal-offers {
  position: relative;
}

.blocal-offers .wp-block-column {
  display: flex;
  flex-direction: column;
}

/*
 * The heading and copy take the free space so every button sits on the same
 * line across the row, regardless of how long the paragraph above it runs.
 */
.blocal-offers .wp-block-column > p {
  flex: 1 1 auto;
}

.blocal-offers .wp-block-buttons {
  margin-top: var(--wp--preset--spacing--40);
}

/*
 * Stacked, the columns need the separation the gap alone does not give them.
 * The columns block supplies the vertical space; this adds the line.
 */
@media (max-width: 781px) {
  .blocal-offers > .wp-block-column + .wp-block-column {
    border-top: 1px solid var(--blocal-hairline);
    padding-top: var(--wp--preset--spacing--50);
  }
}

/* -------------------------------------------------------------------------
 * Long-form measures
 * ---------------------------------------------------------------------- */

/*
 * The homepage caps band paragraphs at 34rem, which is right for a band holding
 * two sentences and wrong for one holding nine. At that measure this page's
 * body copy ran to a narrow gutter down the left of a very tall column.
 *
 * `is-longform` widens the measure to the 44rem the modules above already use,
 * so prose and points share an edge and a right margin.
 */
.blocal-band.is-longform > p,
.blocal-band.is-longform .wp-block-column > p {
  max-width: 44rem;
}

/*
 * A heading that is a question the reader typed sets shorter than the homepage
 * statements, which are claims and want the full 18em to land.
 */
.blocal-band.is-longform :is(h2) {
  max-width: 15em;
}

/*
 * A list added in the editor gets the same measure as the prose around it.
 *
 * Without this a plain core/list matched none of the width rules: paragraphs
 * were capped at 44rem and the list ran the full 1200, so a section read as
 * narrow prose with bullets sticking out 500px past it on both sides. Nothing
 * about that was visible in the pattern files, because the lists there all
 * carry .blocal-points.
 *
 * The cap has to come with the offset rule above, not on its own. Core's
 * constrained layout centers what it does not recognize, so capping the width
 * alone would have moved the list from full-bleed to floating in the middle of
 * the band — correct measure, wrong edge, which is the same bug the headings
 * had.
 *
 * :not(.blocal-points) leaves the styled lists to their own rules, since
 * .is-topics deliberately runs wider than the prose.
 */
.blocal-band.is-longform > .wp-block-list:not(.blocal-points),
.blocal-band.is-longform > .wp-block-quote:not(.blocal-quote) {
  max-width: 44rem;
}

/* -------------------------------------------------------------------------
 * Trio: three parallel items across
 * ---------------------------------------------------------------------- */

/*
 * Three reasons, three arguments, three anything. The same list component as
 * everywhere else, across instead of down.
 *
 * Stacked, three items of thirty-odd words each is a paragraph-shaped block
 * that a reader scans straight past, and the parallelism — the whole reason
 * they are a list — is invisible until you have read all three. Across, the
 * shape does the work.
 *
 * A rule ABOVE each item rather than below. On a single row a bottom rule
 * reads as one line drawn under the section; a top rule caps each column and
 * the three hang from it, which is what says these are three of a kind.
 *
 * Straight to one column below 1000. Falling to two would leave a widow in the
 * second row, and the whole point is that the reader sees all three at once.
 */
@media (min-width: 1000px) {
  .blocal-points.is-trio {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    column-gap: var(--wp--preset--spacing--60);

    /*
     * Capped at the page column, not released entirely. `max-width: none` let
     * this take the band's whole content box — 1344px at a 1440 viewport —
     * while the offset rule had already pushed it in by the wide container's
     * own margin, so it ran 24px past the right edge of the page.
     *
     * min(1200px, 100%) is the same width the offset rule measures against, so
     * the two agree at every viewport.
     */
    max-width: min(1200px, 100%);
  }

  .blocal-points.is-trio > li {
    border-bottom: 0;
    padding-top: var(--wp--preset--spacing--30);
  }

  .blocal-points.is-trio > li:last-child {
    border-bottom: 0;
  }
}

/*
 * Copy on a band that carries a bloom stops before the drawing does.
 *
 * Measured, not guessed. The rhododendron is anchored to the band's right edge
 * at up to 38rem wide, and its ink starts 4.8% in from the left of that box —
 * so anything wider than the band minus the artwork runs underneath it. At the
 * 44rem this page otherwise uses, body copy overlapped the painting by 85px at
 * 1200 and by more as the measure and the drawing both grew.
 *
 * That matters because it is a contrast failure, not a tidiness one. White on
 * the flower's lightest pixel measures 3.54:1, which passes for large text and
 * fails for the 16px body copy that was sitting on it.
 *
 * The 2rem is a gutter between the last glyph and the first petal, so the two
 * do not merely avoid touching but read as separate things.
 */
/*
 * Only where the drawing is actually on screen. Blooms are hidden below 782,
 * and without this query the copy went on reserving space for a picture that
 * was not there: at 375 the band's paragraphs were held to 153px of a possible
 * 335, because --bloom-w still resolved to 40vw and was still being subtracted.
 */
@media (min-width: 782px) {
  .blocal-band.is-longform.blocal-bloom > p,
  .blocal-band.is-longform.blocal-bloom :is(h2),
  .blocal-band.is-longform.blocal-bloom > .blocal-points {
    max-width: min(44rem, 100% - var(--bloom-w) - 2rem);
  }
}

/*
 * H3 is the section's internal signposting — "First, the foundation
 * requirements" — and at the theme's `large` it is the same size as the body
 * copy it introduces. It takes the display face and a little more air above.
 *
 * The size applies to every H3 in the band. The AIR DOES NOT: it is scoped to
 * direct children, because only those are signposts in running copy. An H3
 * that opens a component — a callout card, a column in the offers row — is the
 * first thing inside its own box, and 3rem above it is not separation from the
 * paragraph before, it is a hole between the box's top edge and its title. The
 * callout was carrying 48px of margin on top of 48px of padding.
 */
.blocal-band.is-longform h3 {
  font-size: 1.375rem;
  max-width: 30em;
}

/*
 * A signpost, marked as one.
 *
 * These three headings divide the longest band on the site — twenty-three
 * hundred pixels of continuous prose — and at the shared H3 size they were the
 * same weight as the bold lead-in on every list item around them, so they
 * stopped functioning as stages and read as more emphasis.
 *
 * Bigger, and given a short amber bar above. The bar is the palette's stated
 * use of the accent — "small elements" — and it is the only place on the page
 * where the accent appears as a shape rather than as a stroke under a link, so
 * it reads as structure rather than as decoration. It also carries no text, so
 * amber's 1.91:1 against white is not a contrast question.
 *
 * Direct children only, so a heading that opens a component keeps out of it:
 * the callout card's title and the three offer headings are H3s too, and an
 * amber bar over each would turn a rhythm into a tic.
 */
.blocal-band.is-longform > h3 {
  font-size: 1.75rem;
  margin-top: var(--wp--preset--spacing--60);
}

.blocal-band.is-longform > h3::before {
  content: "";
  display: block;
  width: 3rem;
  height: 3px;
  border-radius: 2px;
  background-color: var(--wp--preset--color--accent);
  margin-bottom: 0.85rem;
}

/*
 * On a dark band the amber still clears anything it needs to, but the first
 * signpost under a heading should not carry the full 5rem of air the ones after
 * it need — there is nothing above it to be separated from but the H2.
 */
.blocal-band.is-longform > :is(h1, h2) + h3 {
  margin-top: var(--wp--preset--spacing--50);
}

/* -------------------------------------------------------------------------
 * Split: narrative one side, a panel the other
 * ---------------------------------------------------------------------- */

/*
 * A section that argues in prose on the left and summarizes in a panel on the
 * right. Different from `.blocal-pathways`, which is two equal columns making
 * two equal offers; here one column is the reading and the other is the
 * reference, so they are deliberately unequal and only one of them is a card.
 *
 * The heading stays outside the columns, as a direct child of the band. Trapped
 * in the left column it would be the narrative's heading rather than the
 * section's, and the panel would top-align against it instead of against the
 * first paragraph.
 */
.blocal-split {
  align-items: flex-start;
}

/*
 * Stacks at 900, not at core's 781.
 *
 * Core's breakpoint assumes columns that share the width evenly. This one is
 * 58/42 with a padded card in the narrow half, so it runs out of room well
 * before core thinks it does: at 782 the card was 275px wide holding 179px of
 * content, and the narrative beside it was down to 380px. Both are past the
 * point where two columns beat one.
 *
 * !important is required, not lazy. The block editor writes the column width
 * as an inline `flex-basis` on the element, and no selector outranks a style
 * attribute.
 *
 * Stacked, the panel goes second, which is already the DOM order.
 */
@media (max-width: 899px) {
  /*
   * `.wp-block-columns` is named as well as the modifier, so this scores two
   * classes against core's one. Core sets `flex-wrap: nowrap` above 782 at a
   * single class, and a tie is settled by whichever stylesheet the browser
   * loaded second — which it did not. The result was columns that took
   * `flex-basis: 100%` and then shrank to fit on one line anyway, so between
   * 782 and 899 they sat side by side at 359px each with the divider already
   * removed. Measured, not guessed.
   */
  .wp-block-columns.blocal-split {
    /*
     * !important is required here rather than lazy, and two classes of
     * specificity were not enough on their own.
     *
     * The columns block generates a per-instance layout class of its own —
     * `.wp-container-core-columns-is-layout-<hash>` — carrying flex-wrap:
     * nowrap, printed inline in the document AFTER the theme stylesheet.
     * Measured at 899: flex-basis: 100% applied to both columns and they
     * shrank to fit on one line anyway, side by side at 359px with the divider
     * already gone.
     */
    flex-wrap: wrap !important;
  }

  .blocal-split > .wp-block-column {
    flex-basis: 100% !important;
  }

  .blocal-split > .wp-block-column + .wp-block-column {
    margin-top: var(--wp--preset--spacing--20);
  }
}

/* -------------------------------------------------------------------------
 * Callout: the section in five lines, for a reader who will not read it
 * ---------------------------------------------------------------------- */

/*
 * A tinted card, not a bordered one. `.blocal-cycle` is already a panel drawn
 * in hairlines and `.blocal-figure` is already an amber rule, so a third
 * outlined box would read as the same component again. A fill says "this is
 * set apart" without adding a color the palette does not have.
 *
 * `surface-alt` also carries black copy at 18.6:1, so nothing inside needs a
 * contrast decision made for it.
 *
 * It sits directly above the full-bleed `surface-alt` band that opens the next
 * section. That is not a collision: this is an inset card with white around it
 * and a 6px radius, and the band below runs edge to edge. The echo is useful —
 * both are asides — but do not give this a border as well, which is what would
 * make the two read as the same object.
 */
.blocal-callout {
  background-color: var(--wp--preset--color--surface-alt);
  border-radius: 6px;

  /*
   * The card states its own text color rather than inheriting.
   *
   * It is a light card, and it is allowed to sit on a dark band — on violet it
   * is the strongest thing on the page. But a dark band sets white text and a
   * white link color on everything inside it, which on this pale blue is
   * 1.1:1 and completely unreadable. The card's ground never changes, so
   * neither should its ink.
   */
  color: var(--wp--preset--color--contrast);

  /*
   * Padding tracks the viewport rather than sitting at a fixed 3rem. The card
   * is 42% of the column, so at the 782px breakpoint a flat 3rem each side
   * left 213px of content inside a 309px card, and at 375 it left 239px of a
   * 335px one. The card was mostly margin.
   *
   * Ceiling is the 3rem it was, so nothing changes on a wide screen.
   */
  padding: clamp(1.5rem, 3.2vw, 3rem);
}

/*
 * The card's own padding is the space above its title. The is-longform H3 rule
 * adds 3rem of margin-top to every H3 in the band, which inside a card is a
 * gap between the top edge and the title rather than separation from anything.
 */
/*
 * Two callout cards side by side, for a pair of statements that are read against
 * each other rather than in sequence.
 *
 * A plain columns block gets most of the way there, but the cards are flex items
 * only at the column level: the group inside is a block child and sizes to its
 * own text, so two sentences of different length produce two different card
 * heights and the pair looks accidental. The column becomes a flex container and
 * the card grows to fill it.
 *
 * Stacks at 899 like .blocal-split and .blocal-duo rather than core's 781,
 * because a card holding a full sentence is unreadable at the widths between.
 */
.blocal-cards .wp-block-column {
  display: flex;
}

.blocal-cards .blocal-callout {
  flex: 1 1 auto;
}

@media (max-width: 899px) {
  /* Two classes deep and !important, for the reason given on .blocal-split: the
     columns block prints its own per-instance nowrap after this stylesheet. */
  .wp-block-columns.blocal-cards {
    flex-wrap: wrap !important;
  }

  .wp-block-columns.blocal-cards > .wp-block-column {
    flex-basis: 100% !important;
  }
}

.blocal-callout > :first-child {
  margin-top: 0;
}

/*
 * Deep enough to beat `.blocal-band.is-longform h3`, which is two classes and a
 * type and would otherwise set this at the signpost size. The card's title is
 * furniture inside a component, not a signpost in the reading.
 */
.blocal-band.is-longform .blocal-callout__title,
.blocal-callout__title {
  font-family: var(--wp--preset--font-family--display);
  max-width: none;
  font-weight: 800;
  font-size: 1.25rem;
  line-height: 1.1;
  letter-spacing: -0.03em;
  margin-bottom: 0;
}

/*
 * The list inside a callout is the same component as the one in the running
 * copy, tightened. Items here are a label and a clause rather than a label and
 * three sentences, so the main list's 1.5rem of padding leaves them floating.
 */
.blocal-callout .blocal-points {
  margin-top: var(--wp--preset--spacing--30);
  max-width: none;
}

.blocal-callout .blocal-points > li {
  padding: 0.8rem 0;
  font-size: 0.9375rem;
  line-height: 1.65;
}

/*
 * The first item's rule would sit immediately under the title and read as an
 * underline on it rather than as the first division in a list.
 */
.blocal-callout .blocal-points > li:first-child {
  border-top: 0;
  padding-top: 0;
}

.blocal-callout .blocal-points > li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

/*
 * Links inside the card follow the card, not the band.
 *
 * !important is required, not lazy: a colored band writes its link color as
 * an inline custom property on the band element and core applies it with
 * `:where()` at a specificity this cannot beat by nesting. Without it a link in
 * a callout on the violet band is white on pale blue.
 */
.blocal-callout a {
  color: var(--wp--preset--color--contrast) !important;
}

/*
 * As a direct child of a band the card is capped, because a five-item summary
 * stretched to the full page column reads as a table rather than as an aside.
 */
.blocal-band.is-longform > .blocal-callout {
  max-width: 40rem;
}

/* -------------------------------------------------------------------------
 * Blooms for the long-form page
 * ---------------------------------------------------------------------- */

/*
 * Both of these anchor right, and that is forced rather than chosen.
 *
 * Every band on /become-a-b-corp/ is left-aligned single-column prose that
 * starts at the band's own left edge. A left-anchored bleed therefore sits
 * directly behind the first words of every line — which is exactly what took
 * the coneflower off the offers band, measured at 333px of heading and a whole
 * column of body copy on the ink.
 *
 * The homepage can alternate sides because its bands are centered or
 * two-column, so there is dead space on one side to bleed into. This page has
 * none. The variation here comes from direction instead: one runs down out of
 * its band, the other runs up out of it.
 *
 * `--bloom-w` is set on the BAND, not on the ::after, so the copy-clearance
 * rule can read it. A variant that declares it on the pseudo-element only will
 * size its artwork correctly and leave the copy capped against the base value.
 */

/*
 * Black-eyed Susan. CURRENTLY UNUSED — kept whole, and working, because the
 * artwork is in the theme and this is the only left-anchored bloom the system
 * has. It needs `.is-copy-right` on its band and the padding reservation below
 * on the band above; all three travel together or none of them do.
 *
 * It was on the standards band until 2026-08-30 and the dogwood replaced it.
 *
 * Replaces the rhododendron, which is the homepage primer's drawing and was
 * the one repeat between the two pages. Yellow on the royal blue is the
 * strongest pairing available in this set, and it rhymes with the amber accent
 * without being it.
 *
 * The file carries 15% of empty space down each side, so anchored flush right
 * the flower would float a seventh of its own width inside the band instead of
 * running off it. The offset pushes it back out.
 */
.blocal-bloom.is-black-eyed-susan {
  --bloom-w: min(32rem, 40vw);
}

.blocal-bloom.is-black-eyed-susan::after {
  /*
   * Left, against the grain of every other bloom on this page.
   *
   * The drawing has its stem sweeping down and to the left, so anchored right
   * it leans out of its own band. It is the one illustration in the set whose
   * composition decides where it goes.
   *
   * A left-anchored bleed can only work where the copy moves out of its way,
   * which is what `.is-copy-right` on the band does. Do not put this variant on
   * a band without that class; it will sit behind the first words of every
   * line, which is what took the coneflower off the offers band.
   */
  right: auto;
  left: 0;

  background-image: url("assets/img/black-eyed-susan.png");
  background-image: image-set(
    url("assets/img/black-eyed-susan.webp") type("image/webp"),
    url("assets/img/black-eyed-susan.png") type("image/png")
  );
  background-position: left calc(var(--bloom-w) * -0.11) top;

  /*
   * Upward, so the flower head clears the top of its own band instead of the
   * stem trailing into the section below it.
   *
   * The band above is the only one on the page with a background of its own,
   * and that turns out to be fine rather than fatal: `.blocal-bloom` puts this
   * band in a stacking context at z-index 1, and the band above is static, so
   * band 5's whole subtree — this pseudo-element included — composites above
   * it. The drawing paints ON the pale tint rather than behind it.
   *
   * The same fact is why the bleed is small. Painting above that band means
   * painting above its text too, so the overhang has to stay inside the 5rem of
   * bottom padding there and never reach the last line. 0.12 of the box is 61px
   * at the widest the drawing ever gets, against 80px of padding.
   *
   * Do not raise the band above to clear its text the way `.blocal-band:has(+
   * .blocal-bloom.is-dogwood)` does. That works for the dogwood because the
   * band above it has no background; doing it here would bring the pale tint up
   * with the text and paint the flower out completely.
   */
  top: calc(var(--bloom-w) * -0.12);
  bottom: 0;
}

/* -------------------------------------------------------------------------
 * Copy pushed clear of a left-hand bloom
 * ---------------------------------------------------------------------- */

/*
 * The band's content starts to the right of the drawing instead of on the
 * page's left edge.
 *
 * This is the only way a left-anchored bleed can live on this page. Every band
 * here is left-aligned prose beginning at the band's own left edge, so a
 * drawing on that side is behind the text unless the text is moved.
 *
 * The margin is the band's usual wide offset PLUS the drawing's width and a
 * 2rem gutter, so the copy begins where the artwork ends rather than at a
 * figure picked by eye — change --bloom-w and the copy follows.
 *
 * The measure is capped against min(1200px, 100%) rather than 100%. 100% is the
 * band's content box, which is wider than the page column, and capping against
 * it would push the right edge of the copy past the page while the left edge
 * was still correct.
 *
 * Four classes deep so it beats the bloom-clearance rule, which is three and
 * would otherwise tie and be decided by source order.
 *
 * Only above 782, because that is where blooms are hidden. Below it the copy
 * returns to the normal left edge — the offset rule is not overridden at all
 * down there, so nothing needs undoing.
 */
@media (min-width: 782px) {
  .blocal-band.is-longform.is-copy-right.is-layout-constrained
    > :is(h1, h2, h3, p, .wp-block-heading, .blocal-points, .blocal-toc,
      .blocal-figure, .blocal-quote, .blocal-cycle, .wp-block-buttons,
      .wp-block-list) {
    margin-left: calc(
      (100% - min(1200px, 100%)) / 2 + var(--bloom-w) + 2rem
    ) !important;
    max-width: min(44rem, min(1200px, 100%) - var(--bloom-w) - 2rem);
  }
}

/*
 * Flowering dogwood, bleeding UP off the North Carolina band.
 *
 * Cornus florida is the state flower, which is why it is on this band and not
 * another: the section is about what North Carolina law does and does not
 * provide, and the drawing is the one piece of decoration on the page that is
 * actually about its subject.
 *
 * Upward, so the two blooms on this page are not the same gesture twice. The
 * bleed is held to a tenth of the box, which keeps it inside the 5rem of
 * bottom padding on the band above and clear of that band's last line.
 *
 * 18% of empty space down the left of this file and 8% down the right, so the
 * offset here is smaller than the Susan's — anchored right, the drawing is
 * already close to the edge.
 */
.blocal-bloom.is-dogwood {
  --bloom-w: min(30rem, 38vw);
}

.blocal-bloom.is-dogwood::after {
  /*
   * This drawing does not bleed. Every other bloom on the site runs past a band
   * edge, and this one is cropped flat across the bottom of its own file — ink
   * to within 0.4% of the frame with no taper — so an overhang cuts it mid-leaf
   * and reads as a rendering fault rather than as a bleed.
   *
   * Sitting it flush instead turns that hard edge into the join between this
   * band and the next, which is the one thing the crop is good for.
   *
   * Because it stays inside its own band it is also the one bloom with no
   * constraint on its neighbors: nothing paints over it and it paints over
   * nothing.
   */
  top: 0;
  bottom: 0;

  background-image: url("assets/img/flowering-dogwood.png");
  background-image: image-set(
    url("assets/img/flowering-dogwood.webp") type("image/webp"),
    url("assets/img/flowering-dogwood.png") type("image/png")
  );
  background-position: right calc(var(--bloom-w) * -0.05) bottom;
}

/*
 * The band a Susan overhangs reserves room for it.
 *
 * Measured without this, band 4's last line cleared the flower's ink by 29px —
 * true, but true by luck. One more sentence in that band's copy moves the text
 * down 28px and puts it under the drawing, and that band is being actively
 * edited.
 *
 * So the space is reserved in the layout instead of being whatever the copy
 * happens to leave over. The band above gains bottom padding worth the overhang
 * again, which makes the clearance a property of the design rather than of the
 * current word count.
 *
 * This is the right way round: the bloom cannot know how long its neighbor's
 * copy is, but the neighbor can always afford the padding.
 */
/*
 * Only where the drawing exists. Blooms are hidden below 782, and without the
 * query the band went on holding 40px of empty space open for a flower that was
 * not being painted.
 */
@media (min-width: 782px) {
  .blocal-band:has(+ .blocal-bloom.is-black-eyed-susan) {
    /*
     * !important is required here rather than lazy. A band's vertical padding is
     * written by the block editor as an inline style attribute on the element,
     * and no selector outranks a style attribute. Without it this rule computed
     * correctly and changed nothing, and the measured clearance stayed at the
     * 29px it was before.
     */
    padding-bottom: calc(var(--wp--preset--spacing--60) + 2.5rem) !important;
  }
}

/*
 * The dogwood no longer bleeds, so the band above it needs no protection. The
 * rule that used to lift that band out of the way is gone rather than left
 * inert — a z-index with nothing to do is the kind of thing someone later
 * builds on by mistake.
 */

/* -------------------------------------------------------------------------
 * Duo: two questions that are really one question
 * ---------------------------------------------------------------------- */

/*
 * Cost and time are two separate searches — "b corp certification cost" and
 * "how long does b corp certification take" — so they keep two H2s and stay
 * two headings a search engine can find separately. But a reader asking one is
 * always asking the other, and stacked they read as two sections with a
 * screenful between them.
 *
 * Equal columns, unlike `.blocal-split`, because neither question is the
 * subordinate of the other. A hairline between them, drawn the way
 * `.blocal-pathways` draws its own: as a pseudo-element on the wrapper rather
 * than a border on the second column, since a border sits on that column's edge
 * and lands hard against its heading instead of centered in the gap.
 */
.blocal-duo {
  position: relative;
  align-items: flex-start;
}

@media (min-width: 900px) {
  .blocal-duo::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--blocal-hairline);
  }
}

/*
 * Stacks at 900 rather than core's 781, for the same reason `.blocal-split`
 * does: two headings at display scale need more room than core's breakpoint
 * assumes. The inline flex-basis the editor writes needs !important to move.
 */
@media (max-width: 899px) {
  /* Two classes deep, for the reason given on `.blocal-split` above. */
  .wp-block-columns.blocal-duo {
    /*
     * !important is required here rather than lazy, and two classes of
     * specificity were not enough on their own.
     *
     * The columns block generates a per-instance layout class of its own —
     * `.wp-container-core-columns-is-layout-<hash>` — carrying flex-wrap:
     * nowrap, printed inline in the document AFTER the theme stylesheet.
     * Measured at 899: flex-basis: 100% applied to both columns and they
     * shrank to fit on one line anyway, side by side at 359px with the divider
     * already gone.
     */
    flex-wrap: wrap !important;
  }

  .blocal-duo > .wp-block-column {
    flex-basis: 100% !important;
  }

  .blocal-duo > .wp-block-column + .wp-block-column {
    border-top: 1px solid var(--blocal-hairline);
    padding-top: var(--wp--preset--spacing--50);
  }
}

/*
 * A heading in a half-width column sets smaller than one that owns the band.
 *
 * At the page's x-large, "How much does B Corp certification cost?" runs to
 * four lines in half a column and the two headings between them fill a screen
 * before either answer starts. This keeps them clearly H2s and clearly larger
 * than the H3 signposts, without letting them take the whole band.
 */
.blocal-band.is-longform .blocal-duo h2 {
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  max-width: 14em;
}

/*
 * The first heading in each column has the columns' own gap above it already.
 */
.blocal-duo > .wp-block-column > h2:first-child {
  margin-top: 0;
}

/* -------------------------------------------------------------------------
 * Resources
 * ---------------------------------------------------------------------- */

/*
 * A sequenced list of twelve links, not a directory of thirty. The design job
 * is to let a reader skip an entry without opening it, which is why every item
 * carries a one-line "who this is for" and says whether it will ask them to
 * sign up.
 */
.blocal-resources__stage {
  margin-top: var(--wp--preset--spacing--60);
  max-width: 15em;
}

.blocal-resources > .blocal-resources__stage:first-child {
  margin-top: 0;
}

.blocal-resources__list {
  list-style: none;
  margin: var(--wp--preset--spacing--40) 0 0;
  padding: 0;
  max-width: 46rem;
}

/*
 * The number sits in its own column rather than as a list marker, so the body
 * text of every entry starts on one edge no matter whether the count is one
 * digit or two. A real <ol> underneath, so the sequence survives being read
 * without styles.
 */
.blocal-resources__item {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: 0 var(--wp--preset--spacing--30);
  padding: var(--wp--preset--spacing--40) 0;
  border-top: 1px solid var(--blocal-hairline);
}

.blocal-resources__item:last-child {
  border-bottom: 1px solid var(--blocal-hairline);
}

/*
 * The count is decoration over a real ordered list, so it is hidden from
 * assistive technology in the markup and styled as furniture here: display
 * sans, not the body serif, like every other number on the site.
 */
.blocal-resources__n {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 800;
  font-size: 1.125rem;
  line-height: 1.5;
  letter-spacing: -0.02em;
  color: var(--wp--preset--color--muted);
}

.blocal-resources__title {
  font-size: 1.125rem;
  margin: 0;
  max-width: none;
}

.blocal-resources__desc p {
  margin: 0.5rem 0 0;
  max-width: none;
}

/*
 * The qualifying line. Small, set in the sans, and deliberately quiet: it is
 * apparatus for deciding whether to click, not part of the description.
 *
 * `muted` is black at 62%, a light-ground color only. This band is on the page
 * ground and must stay there — on violet the same value measures 3.89 and fails.
 */
.blocal-resources__meta {
  margin: 0.55rem 0 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.5;
  color: var(--wp--preset--color--muted);
  max-width: none;
}

/*
 * The freshness line, at the foot of the page. Same muted treatment, because a
 * reader should be able to find it and then stop thinking about it.
 */
.blocal-resources__reviewed {
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  color: var(--wp--preset--color--muted);
}

@media (max-width: 600px) {
  /*
   * The number column goes, rather than shrinking. At this width 2.5rem of
   * gutter is a tenth of the screen spent on a decorative digit.
   */
  .blocal-resources__item {
    grid-template-columns: 1fr;
  }

  .blocal-resources__n {
    display: none;
  }
}

/* -------------------------------------------------------------------------
 * Navigation submenu
 * ---------------------------------------------------------------------- */

/*
 * Core ships a submenu that works and looks like nothing in particular: square,
 * borderless, and inheriting whatever the header happens to set. This gives it
 * the same vocabulary as the rest of the site — hairline border, 6px corner,
 * display sans — so it reads as part of the design rather than as the block
 * editor showing through.
 */
/*
 * `.has-child` is named so this outranks core's own rule, which is three
 * classes deep and sets z-index: 2. Without it the declaration below computed
 * as 2 and the comment about stacking contexts described something that was
 * not happening. It survives today because the menu opens over the hero, which
 * sets no z-index — but a bloom band takes 1 and the band after it takes 2, so
 * a longer menu or a shorter header would put the panel behind the page.
 */
.wp-block-navigation .wp-block-navigation-item.has-child > .wp-block-navigation__submenu-container {
  background-color: var(--wp--preset--color--base);
  border: 1px solid var(--wp--preset--color--rule);
  border-radius: 6px;
  padding: 0.35rem;
  min-width: 13rem;

  /*
   * The header sits above the page but sets no stacking context of its own, and
   * the bands below it do — .blocal-bloom takes z-index 1 and the band after it
   * takes 2. Without this the menu opens behind the first colored band on the
   * page, which is exactly where it opens on the B Corp page.
   */
  z-index: 30;

  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.12);
}

.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
  font-family: var(--wp--preset--font-family--display);
  padding: 0.55rem 0.7rem;
  border-radius: 4px;
  width: 100%;
}

/*
 * A tinted row on hover rather than the drawn brush the top level uses. The
 * stroke is sized to a word sitting in running text; under a menu row it
 * underlines the label and leaves the rest of the row looking accidental.
 */
.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content:hover,
.wp-block-navigation .wp-block-navigation__submenu-container .wp-block-navigation-item__content:focus-visible {
  background-image: none;
  background-color: var(--wp--preset--color--surface-alt);
}

/*
 * The parent is a <button> once submenus open on click, and a button carries
 * none of the anchor styling the rest of the nav has.
 */
.wp-block-navigation .wp-block-navigation-submenu__toggle {
  font-family: var(--wp--preset--font-family--display);
  font-weight: inherit;
  font-size: inherit;
  cursor: pointer;
}

/*
 * Inside the mobile overlay the submenu is not a floating panel, it is an
 * indented part of the list. A border, a shadow and a white ground there would
 * draw a small card inside a full-screen menu.
 */
/*
 * Matched at the same depth as the panel rule above plus the overlay class.
 *
 * Raising that rule to four classes so it could outrank core's z-index made it
 * outrank THIS one too, and the mobile menu kept the drop shadow and the 200px
 * min-width it is here to remove. Specificity fixes travel: a rule raised to
 * win one argument starts winning arguments it was never in.
 */
.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation-item.has-child
  > .wp-block-navigation__submenu-container {
  background-color: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding-left: var(--wp--preset--spacing--30);
  min-width: 0;
}

.wp-block-navigation__responsive-container.is-menu-open
  .wp-block-navigation-item.has-child
  > .wp-block-navigation__submenu-container
  .wp-block-navigation-item__content:hover {
  background-color: transparent;
  background-image: var(--blocal-brush);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 0.3em;
}

/* -------------------------------------------------------------------------
 * Interior page hero
 * ---------------------------------------------------------------------- */

/*
 * A hero for the pages below the flagship: Resources today, and About, Connect,
 * Join, Give and the grant page as they arrive.
 *
 * Tinted, not black. The B Corp page's solid hero carries a promise — the whole
 * process on one page — and these carry an instruction. Giving every page the
 * flagship treatment would stop the flagship reading as one, and a child page
 * sitting quieter than its parent is the correct order of things.
 *
 * The tint is also what makes the contents rail legal. That list is small type,
 * and small type is the one thing this palette's dark grounds cannot all carry:
 * softened white on violet measures 3.89 and fails. On this ground the rail is
 * black at 18.6:1 and no per-band contrast decision is needed.
 */
.blocal-page-hero {
  border-bottom: 1px solid var(--blocal-hairline);
}

/*
 * The copy keeps the reading measure it would have had anyway. Without this it
 * would stretch to the column, which at 58% of 1200 is wider than the 44rem
 * the rest of the site holds prose to.
 */
.blocal-page-hero .wp-block-column > p {
  max-width: 34rem;
}

/* -------------------------------------------------------------------------
 * On this page, as a rail
 * ---------------------------------------------------------------------- */

/*
 * The same component as the contents strip on the B Corp page — uppercase
 * label, hairline under each item, display sans — turned on its side to sit
 * beside the hero copy rather than under it.
 *
 * Single column, always. `.blocal-toc` fans out to three across a full band;
 * in a 42% rail that would give three columns of about nine characters.
 */
.blocal-contents {
  max-width: 26rem;
}

.blocal-contents__label {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--wp--preset--color--muted);
}

.blocal-contents__list {
  list-style: none;
  margin: var(--wp--preset--spacing--30) 0 0;
  padding: 0;
}

.blocal-contents__list > li {
  border-bottom: 1px solid var(--blocal-hairline);
}

/*
 * Deep enough to beat `.blocal-band p a` and `.wp-block-list a`, both of which
 * are a class plus a type and would otherwise paint an amber stroke under every
 * item at rest. Nine of those is a thicket; the stroke arrives on hover, which
 * is the same bargain the site navigation makes.
 */
/*
 * Only the padding lives here. Everything else about these links is shared with
 * the contents strip further up this file, so the two cannot drift apart again.
 *
 * 0.6rem gives a 40px row, which is a comfortable target on a touch screen and
 * the reason the two components differ at all.
 */
.blocal-contents .blocal-contents__list a {
  padding: 0.6rem 0;
}

/*
 * Stacked, the rail becomes a strip under the copy and wants the air the column
 * gap was giving it. It keeps its own hairline above so it still reads as
 * apparatus rather than as the first section of the page.
 */
@media (max-width: 899px) {
  .blocal-contents {
    max-width: none;
    padding-top: var(--wp--preset--spacing--40);
    border-top: 1px solid var(--blocal-hairline);
  }
}

/* -------------------------------------------------------------------------
 * Checklist
 * ---------------------------------------------------------------------- */

/*
 * The points list with a tick in the margin, for a list a reader is meant to
 * test themselves against rather than read through.
 *
 * The tick is drawn in the accent, which is the palette's stated use for small
 * elements. It carries no text, so amber's 1.91:1 against white is not a
 * contrast question — and it is repeated in the item's own words anyway, so it
 * is hidden from assistive technology rather than read out four times.
 */
/*
 * The tick sits in its own column rather than being positioned over the text.
 *
 * It was absolute, offset from the top of the item by the item's own padding
 * plus a guess — and the guess was wrong for the first item, whose tick rode a
 * full 1.5rem high because the rule assumed it had no top padding. It has the
 * same padding as every other item.
 *
 * A grid column cannot drift. The tick is a grid item beside the text, aligned
 * to the top of it, so nothing has to know what padding the row carries or how
 * many lines the text runs to.
 *
 * The inline content — a bold lead-in and the sentence after it — is all inline,
 * so the grid wraps it into a single anonymous item. Two columns, two items.
 */
/*
 * NOT a grid, and not flex either. Both blockify every in-flow child, so the
 * bold lead-in becomes a grid item of its own and the sentence following it
 * becomes a THIRD item — which lands in the next row's tick column and wraps
 * one word per line. The item's content is deliberately inline and has to stay
 * that way, so the tick is positioned over the row rather than placed in it.
 *
 * The offset is the row's own top padding plus a nudge to the cap height of the
 * first line. Every item carries that padding, including the first — an earlier
 * :first-child override assumed otherwise and floated its tick a clear 1.5rem
 * above the words.
 */
.blocal-points.is-checklist > li {
  position: relative;
  padding-left: 2.4rem;
}

.blocal-points.is-checklist > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--wp--preset--spacing--40) + 0.42em);
  width: 1.15rem;
  height: 0.6rem;
  border-left: 2.5px solid var(--wp--preset--color--accent);
  border-bottom: 2.5px solid var(--wp--preset--color--accent);
  transform: rotate(-45deg);
}

/*
 * Two across, once there is room. Four criteria stacked is a tall column of
 * mostly white space beside a short line of text, and a reader testing
 * themselves against a list is helped by seeing all of it at once.
 *
 * Capped rather than released — `max-width: none` on a gridded list is what
 * sent the trio 24px past the right edge of the page.
 */
@media (min-width: 900px) {
  .blocal-points.is-checklist {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--wp--preset--spacing--60);
    max-width: min(62rem, 100%);
  }

  /*
   * In a grid the top border no longer draws one continuous line down a single
   * column, so each item closes itself instead. Same reasoning as .is-topics.
   */
  .blocal-points.is-checklist > li {
    border-top: 0;
    border-bottom: 1px solid var(--blocal-hairline);
  }
}


/* -------------------------------------------------------------------------
 * A form in a column
 * ---------------------------------------------------------------------- */

/*
 * Gravity Forms sizes its controls off its own custom properties, and the
 * defaults assume a form has a page to itself. In a 62% column the inputs still
 * stretch to the column and the labels still set at body width, which is fine —
 * what is not fine is the gap between fields, tuned for a full-width form and
 * leaving a three-page application taller than the copy beside it by half a
 * screen.
 *
 * Sizing is set through Orbital's own variables rather than with !important.
 * That is the documented difference: its COLORS need !important, its sizes do
 * not, and reaching for !important here would override the theme's own
 * responsive behavior along with the value being changed.
 */
.blocal-split .gform_wrapper {
  --gform-theme-field-margin-block-end: 1.1rem;
}

/*
 * The submit and page-navigation buttons sit left in a column rather than
 * filling it. A full-width button inside a narrow column reads as a banner.
 */
.blocal-split .gform_wrapper .gform_footer,
.blocal-split .gform_wrapper .gform_page_footer {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wp--preset--spacing--20);
  justify-content: flex-start;
}

.blocal-split .gform_wrapper .gform_footer input[type="submit"],
.blocal-split .gform_wrapper .gform_page_footer input[type="button"],
.blocal-split .gform_wrapper .gform_page_footer input[type="submit"] {
  width: auto;
}

/* -------------------------------------------------------------------------
 * Banner holder
 * ---------------------------------------------------------------------- */

/*
 * The chapter's own tradition, and the one thing on a page made of numbers that
 * shows rather than tells.
 *
 * The card overlaps the photograph rather than sitting beside it. Set side by
 * side the two left a third of the band empty and read as a picture with a
 * caption filed next to it; overlapping makes them one object, and the amber
 * does the work of saying this is the thing to look at.
 */
.blocal-banner-holder {
  position: relative;
}

.blocal-banner-holder__photo {
  margin: 0;
}

.blocal-banner-holder__photo img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 6px;
}

/*
 * Amber with black on it. That pair is 11.02:1 and the strongest contrast the
 * palette has — which is why the accent can carry running text here when it
 * cannot anywhere else on the site. White on this amber is 1.91 and fails, so
 * nothing in this card may be reversed.
 */
.blocal-banner-holder__card {
  background-color: var(--wp--preset--color--accent);
  color: var(--wp--preset--color--contrast);
  border: 1px solid var(--wp--preset--color--contrast);
  border-radius: 6px;
  padding: var(--wp--preset--spacing--40);
}

.blocal-banner-holder__label {
  margin: 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  max-width: none;
}

.blocal-banner-holder__name {
  margin: 0.4rem 0 0;
  font-family: var(--wp--preset--font-family--display);
  font-weight: 800;
  font-size: 1.375rem;
  line-height: 1.1;
  letter-spacing: -0.03em;
  max-width: none;
}

.blocal-banner-holder__when {
  margin: 0.5rem 0 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  max-width: none;
}

/*
 * The drawn amber underline is invisible on an amber ground, so the link inside
 * this card takes a plain rule in ink instead. Deep enough to beat
 * `.blocal-band p a`, which is a class plus a type.
 */
.blocal-banner-holder .blocal-banner-holder__name a {
  background-image: none;
  padding-bottom: 0;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.18em;
  color: var(--wp--preset--color--contrast);
}

.blocal-banner-holder .blocal-banner-holder__name a:hover {
  background-image: none;
  text-decoration-thickness: 3px;
}

/*
 * Overlapped only where there is room to overlap. The photograph takes two
 * thirds and the card starts before its right edge, so it sits partly on the
 * image and partly in the space that was empty — which is what stops it
 * needing to hang off the page to overhang anything.
 */
@media (min-width: 900px) {
  .blocal-banner-holder__photo {
    width: 66%;
  }

  .blocal-banner-holder__card {
    position: absolute;
    left: 56%;
    bottom: var(--wp--preset--spacing--50);
    width: min(38%, 26rem);
    box-shadow: 0 12px 34px rgba(0, 0, 0, 0.16);
  }
}

/*
 * Stacked, it still overlaps — pulled up over the foot of the photograph and
 * inset from both sides, so the relationship survives the layout change rather
 * than becoming a caption again.
 */
@media (max-width: 899px) {
  .blocal-banner-holder__card {
    position: relative;
    margin: -3rem var(--wp--preset--spacing--30) 0;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16);
  }
}

/* -------------------------------------------------------------------------
 * The board
 * ---------------------------------------------------------------------- */

/*
 * Three people, named, with faces. The old About page named two and showed
 * neither, which is the first thing a funder doing diligence notices.
 *
 * The photo slots are placeholders and look like placeholders on purpose. A
 * board section with three grey boxes reads as unfinished, which it is; one
 * with three initials-in-a-circle reads as finished and wrong, and nobody
 * chases it.
 */
/*
 * The slot is a fixed 12rem square, and the markup says so with sizes="192px".
 *
 * Without it WordPress writes sizes="(max-width: 768px) 100vw, 768px" from the
 * registered image size, and a browser dutifully fetches a 768px file to paint
 * 192 — four times the pixels on every board photograph. The number here and
 * the number in the markup have to move together.
 *
 * The markup also sets loading="eager" on all three. WordPress marks the later
 * images on a page lazy and, when it does, prepends `auto` to the sizes
 * attribute — and `auto` resolved before layout, fetching a 1439px file for the
 * same 192px slot. Three thumbnails do not need lazy loading, and eager is what
 * keeps the sizes hint above meaning what it says.
 */
.blocal-board__photo {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  max-width: 12rem;
  margin: 0 0 var(--wp--preset--spacing--30);
  border-radius: 6px;
  overflow: hidden;
}

/*
 * A real photograph fills the square and needs no frame. The dashed box below
 * is only for the slots still waiting on one, so the two states cannot be
 * confused with each other at a glance.
 */
.blocal-board__photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 6px;
}

/*
 * Still a placeholder. It looks like one on purpose: a board section with an
 * empty slot reads as unfinished, which it is, where initials in a circle read
 * as finished and wrong and nobody chases them.
 */
p.blocal-board__photo {
  border: 1px dashed var(--blocal-hairline);
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  letter-spacing: 0.06em;
  color: var(--wp--preset--color--muted);
}

.blocal-board h3 {
  margin: 0;
  font-size: 1.25rem;
  max-width: none;
}

.blocal-board__role {
  margin: 0.35rem 0 0;
  font-family: var(--wp--preset--font-family--display);
  font-size: var(--wp--preset--font-size--small);
  line-height: 1.5;
  color: var(--wp--preset--color--muted);
  max-width: none;
}

/*
 * Three abreast on a wide screen, one column below. Core's own breakpoint is
 * fine here: unlike the split and the duo, these columns are equal and hold
 * very little, so they survive being narrow.
 */
@media (max-width: 781px) {
  .blocal-board > .wp-block-column + .wp-block-column {
    margin-top: var(--wp--preset--spacing--40);
  }
}

/*
 * Swamp milkweed, on the About page's record of what the chapter has done.
 *
 * It does not bleed, and cannot: both of that band's neighbors are tinted, and
 * a bleed onto a band with a background of its own is painted over. It also
 * cannot sit at the bottom right where the others do, because the banner-holder
 * photograph and its caption occupy the full width down there.
 *
 * So it hangs from the top instead, in the only clear region on that band — to
 * the right of copy capped at 44rem and above the photograph. Anchored to the
 * TOP rather than the bottom on purpose: measured at 1440 it ends 588px above
 * the photo row, and anchoring from the top means adding copy to the list
 * pushes that row further away rather than closer.
 *
 * The file carries under 5% of empty space down each side, so anchored right it
 * is already near the edge and needs only a nudge.
 */
.blocal-bloom.is-milkweed {
  /*
   * The largest bloom on the site, and the band can carry it: 1,633px tall with
   * its whole upper right free. At 24rem it read as a sticker on a wall.
   *
   * This is the size at which the clearance rule starts costing something. The
   * copy measure is `min(44rem, 100% - --bloom-w - 2rem)`, so past roughly this
   * width the drawing stops taking empty space and starts taking the reading
   * column instead. Going bigger means deciding to narrow the prose, which is a
   * different decision from making the flower bigger.
   */
  --bloom-w: min(40rem, 48vw);
}

.blocal-bloom.is-milkweed::after {
  top: var(--wp--preset--spacing--60);
  bottom: auto;
  right: 0;
  height: var(--bloom-w);

  background-image: url("assets/img/swamp-milkweed.png");
  background-image: image-set(
    url("assets/img/swamp-milkweed.webp") type("image/webp"),
    url("assets/img/swamp-milkweed.png") type("image/png")
  );
  /*
   * Pushed past the right edge so it runs off the screen rather than sitting
   * politely inside it.
   *
   * Done by moving the IMAGE within its box, not by moving the box. The box
   * still ends exactly at the band's right edge, which is the viewport edge, so
   * the drawing is clipped there and nothing widens the document. Moving the
   * box instead would add a horizontal scrollbar, and that cannot be hidden —
   * overflow-x: hidden forces overflow-y to auto and would cut off every
   * vertical bleed on the site.
   */
  background-position: right calc(var(--bloom-w) * -0.18) top;
  background-size: var(--bloom-w) auto;
}

/* -------------------------------------------------------------------------
 * Membership tiers
 * ---------------------------------------------------------------------- */

/*
 * A real table, because that is what this is: eleven attributes compared across
 * three levels. A grid of divs would look the same and tell a screen reader
 * nothing about which column a "Yes" belongs to.
 *
 * Two of the three levels cost the same $100 and differ by eligibility rather
 * than by price, so the dues row cannot carry the whole story on its own — the
 * "who it is for" row directly under it is doing as much work and is styled to
 * match rather than as an afterthought.
 */
.blocal-tiers {
  margin: var(--wp--preset--spacing--50) 0 0;

  /*
   * Wide content scrolls inside its own container. Eleven rows of three columns
   * will not fit a phone, and the alternative — letting the page scroll
   * sideways — is the one thing this build has consistently refused.
   */
  overflow-x: auto;
  max-width: min(62rem, 100%);
}

.blocal-tiers table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.blocal-tiers th,
.blocal-tiers td {
  text-align: left;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--blocal-hairline);
  vertical-align: top;
}

/* The row labels are the reading column; the tier columns are the answers. */
.blocal-tiers tbody th,
.blocal-tiers tbody td:first-child {
  font-family: var(--wp--preset--font-family--body);
  font-weight: 400;
  width: 40%;
}

/*
 * Tier names, set as furniture rather than as prose: display sans, and sitting
 * on a heavier rule so the header reads as a header when the table is scrolled.
 */
.blocal-tiers thead th {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 800;
  font-size: 1.125rem;
  letter-spacing: -0.02em;
  border-bottom: 2px solid var(--wp--preset--color--contrast);
  padding-bottom: 0.6rem;
}

/*
 * The dues row is the first thing anyone looks for, so it gets the size to be
 * found without being hunted for.
 */
.blocal-tiers tbody tr:first-child td,
.blocal-tiers tbody tr:first-child th {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 800;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  padding-top: 0.9rem;
  padding-bottom: 0.9rem;
}

/*
 * The eligibility row, given weight to match. Two levels cost the same $100 and
 * differ only here, so a reader comparing on price alone learns nothing — this
 * row is the answer to "which one am I".
 */
.blocal-tiers tbody tr:nth-child(2) td,
.blocal-tiers tbody tr:nth-child(2) th {
  font-family: var(--wp--preset--font-family--display);
  font-weight: 700;
  font-size: 1rem;
}

/* Zebra striping in the palette's own tint rather than a grey it does not have. */
.blocal-tiers tbody tr:nth-child(even) {
  background-color: var(--wp--preset--color--surface-alt);
}

/*
 * A scrollable region has to be reachable from the keyboard, or the columns
 * past the fold are unreachable without a mouse.
 */
.blocal-tiers:focus-visible {
  outline: 2px solid var(--wp--preset--color--contrast);
  outline-offset: 2px;
}

/* -------------------------------------------------------------------------
 * Affiliation marks
 * ---------------------------------------------------------------------- */

/*
 * A narrow column at the left of the primer band holding the Certified B Corp
 * badge and the B Local mark, stacked.
 *
 * Fixed 150px rather than a percentage: these are supplied artwork at a fixed
 * aspect, and a percentage column would resize them at every breakpoint. The
 * copy column takes what is left, and its own 30rem measure cap still applies,
 * so the pair together stay clear of the rhododendron bleeding in on the right.
 */
.blocal-primer__marks {
  display: flex;
  flex-direction: column;
  gap: var(--wp--preset--spacing--40);
  align-items: flex-start;
}

.blocal-primer__marks :is(figure, .wp-block-image) {
  margin: 0;

  /*
   * 100px, not the 110px this started at. The supplied black badge is 187px
   * wide, so 100px renders it at 1.87x and stays crisp on a 2x display; at
   * 110px it would be upscaling on those screens.
   */
  max-width: 100px;
}

.blocal-primer__marks img {
  display: block;
  width: 100%;
  height: auto;
}

/*
 * Stacked, the marks sit in a row above the copy rather than in a column beside
 * it, so they do not eat a third of a phone's width.
 */
@media (max-width: 781px) {
  .blocal-primer__marks {
    flex-direction: row;
    align-items: center;
    gap: var(--wp--preset--spacing--50);
  }

  .blocal-primer__marks :is(figure, .wp-block-image) {
    max-width: 84px;
  }
}
