
/* ==========================================================================
   Liquid Breathwork facilitator directory
   Deep Surrender tokens only. Token definitions live in /css/variables.css.
   ========================================================================== */

*,
*::before,
*::after { margin: 0; padding: 0; box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--cream);
  /* The site's body is a FIXED 135deg gradient, not flat navy. Without it the
     directory's navy reads deader than every other page on the site.
     The colour under the image is declared separately and deliberately: a fixed
     image only paints the viewport, so anything that renders the page taller
     than one screen (a full-page screenshot, a print) shows the colour instead
     of the browser's white canvas. */
  background-color: var(--abyss);
  background-image: linear-gradient(135deg, var(--abyss) 0%, var(--deep-water) 50%, var(--abyss) 100%);
  background-attachment: fixed;
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* The default panel surface. .section--alt swaps it; nothing else does. */
  --panel: var(--undertow-60);
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  line-height: var(--lh-heading);
  margin-bottom: var(--space-md);
}

h1 { font-size: var(--fs-h1); color: var(--cream);     margin-bottom: var(--space-lg); }
h2 { font-size: var(--fs-h2); color: var(--warm-sand); margin-bottom: var(--space-xl); }
h3 { font-size: var(--fs-h3); color: var(--warm-sand); margin-bottom: var(--space-lg); }
h4 { font-size: var(--fs-h4); color: var(--gold);      margin-bottom: var(--space-md); }

p {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  margin-bottom: var(--space-lg);
  color: var(--cream);
}

a { color: var(--gold); text-decoration: none; transition: color var(--ease); }
a:hover { color: var(--warm-sand); }
/* The site distinguishes links by colour alone. Keyboard focus gets an underline
   as well, so the focused link is identifiable without relying on hue. */
a:focus-visible { text-decoration: underline; }

strong { font-weight: var(--fw-semibold); }
em { font-style: italic; }

ul, ol { list-style: none; }

img { max-width: 100%; height: auto; display: block; }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Skip link. Real link, real focus, no JavaScript.
   -------------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: var(--space-sm) var(--space-md);
  background: var(--gold);
  color: var(--abyss);
  z-index: 300;
}
.skip-link:focus { left: var(--space-md); top: var(--space-md); }

/* --------------------------------------------------------------------------
   Sections. Navy and sand, alternating. There is no third background.

   Vertical padding only: the horizontal 5% belongs to .wrap, exactly as it
   belongs to the site's .container, so a full-bleed sand band reaches both
   edges of the viewport while its content stays on the grid.
   -------------------------------------------------------------------------- */
.section {
  position: relative;
  padding: var(--space-section) 0;
  /* The body's gradient, repeated and pinned to the viewport so every navy
     section samples the same one and the page reads as a single surface. It is
     declared here rather than left transparent so a navy section still has an
     explicit background of its own, which is what makes "navy or sand, never a
     third" an invariant a test can check. Where an engine downgrades a fixed
     attachment to scroll (iOS), each section paints the same gradient, starting and ending on
     --abyss, so the seams stay invisible. */
  background-color: var(--abyss);
  background-image: linear-gradient(135deg, var(--abyss) 0%, var(--deep-water) 50%, var(--abyss) 100%);
  background-attachment: fixed;
  /* The hairline under every section. Part of why the site reads as one system. */
  border-bottom: 1px solid var(--gold-10);
}

.section--alt {
  background: var(--warm-sand);
  color: var(--abyss);
  border-top: 1px solid var(--gold-20);
  border-bottom: 1px solid var(--gold-20);
  /* Glass over sand is mud. Panels on this section go solid navy instead. */
  --panel: var(--deep-water);
}

.section--alt h1, .section--alt h2 { color: var(--abyss); }
.section--alt h3 { color: var(--deep-water); }
.section--alt h4 { color: var(--undertow); }
.section--alt p  { color: var(--deep-water); }
.section--alt .eyebrow { color: var(--undertow); }

/* Gold on sand is banned as body text. Links on sand get the deep navy and an
   underline so they are still obviously links without the contrast failure.

   :where(), NOT a chain of :not(). This rule has to LOSE to the dark-panel rules
   below it, and every :not() argument adds a class's worth of specificity: the
   site's own a:not(.btn-primary):not(.nav-cta) scores higher than
   .section--alt .card__name a, which is precisely how every card title in the
   grid came out gold and underlined. :where() contributes nothing, so this stays
   at one class and the panel rules win on merit. */
.section--alt a:where(:not(.btn, .btn-primary, .btn--ghost, .btn-ghost, .nav-cta)) {
  color: var(--undertow);
  text-decoration: underline;
}
.section--alt a:where(:not(.btn, .btn-primary, .btn--ghost, .btn-ghost, .nav-cta)):hover { color: var(--abyss); }

/* --------------------------------------------------------------------------
   ...EXCEPT inside a dark panel sitting on that light section.

   THE PANEL'S BACKGROUND DECIDES THE TEXT COLOUR, NOT THE SECTION'S. The rules
   above target elements directly, so they beat the cream a panel passes down by
   inheritance, and the result is navy on navy: the "No facilitators match that
   yet" heading was very nearly invisible on the live directory, and Ryan caught
   it in a screenshot. This block is the general fix, listing every dark panel
   once instead of patching them one bug at a time.
   -------------------------------------------------------------------------- */
.section--alt :is(.card, .filters, .panel-list > li, .empty, .notice, .relay-form, .event-card, .pagination__page, .pagination__step),
.section--alt :is(.card, .filters, .panel-list > li, .empty, .notice, .relay-form, .event-card) :is(h1, h2, h3, h4, p, li, dt, dd, summary, caption) {
  color: var(--cream);
}
/* A form label already reads --warm-sand on any dark surface, and that sand is
   the site's own label colour. Left out of the rule above on purpose: cream
   labels would be a directory-only invention. */
.section--alt :is(.card, .filters, .panel-list > li, .empty, .notice, .relay-form) :is(.eyebrow, .skills__label) { color: var(--gold); }
/* The date badge keeps its gold month and cream day inside the dark panel,
   whichever colour the section behind the panel is. */
.section--alt .event-card__month { color: var(--gold); }
.section--alt .event-card__day { color: var(--cream); }

/* ...and a link inside a dark panel gets the dark-surface treatment, whatever
   colour the section behind the panel is. That is the bug Ryan saw: facilitator
   names in the grid rendering as underlined links that "read like raw HTML". */
.section--alt .card a,
.section--alt .panel-list a,
.section--alt .empty a,
.section--alt .notice a,
.section--alt .event-card a,
.section--alt .pagination a {
  color: var(--gold);
  text-decoration: none;
}

.section--alt .card a:hover,
.section--alt .card a:focus-visible,
.section--alt .panel-list a:hover,
.section--alt .panel-list a:focus-visible,
.section--alt .empty a:hover,
.section--alt .empty a:focus-visible,
.section--alt .event-card a:hover,
.section--alt .event-card a:focus-visible,
.section--alt .pagination a:hover,
.section--alt .pagination a:focus-visible { text-decoration: underline; }

/* The card title is the card's primary action, so it reads as a heading in the
   card's own text colour and turns gold on hover. It is still distinguishable
   without colour: it is the only heading-sized text in the card, and it gains an
   underline on hover and on keyboard focus. */
.card__name a,
.section--alt .card__name a {
  color: var(--cream);
  text-decoration: none;
  /* Padding, not a bigger font: the title is the card's primary action and a
     24px line box is under the tap-target floor. */
  display: inline-block;
  padding: 0.25rem 0;
}
.card__name a:hover,
.card__name a:focus-visible,
.section--alt .card__name a:hover,
.section--alt .card__name a:focus-visible {
  color: var(--gold);
  text-decoration: underline;
}

/* --------------------------------------------------------------------------
   Containers and section rhythm
   -------------------------------------------------------------------------- */
.wrap {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 5%;
}

/* Reading measure. Body copy never spans the full container on wide screens. */
.narrow {
  max-width: var(--max-width-text);
  margin-left: auto;
  margin-right: auto;
}

/* The site's signature section intro: eyebrow, centred h2, 48px gold rule. */
.section-header {
  text-align: center;
  margin-bottom: var(--space-2xl);
}
.section-header h2 { margin-bottom: var(--space-xs); }
.section-header p { margin-left: auto; margin-right: auto; max-width: var(--max-width-text); }

.gold-line {
  width: 48px;
  height: 2px;
  background: var(--gold);
  margin-bottom: var(--space-lg);
  /* Decorative rule, not a divider anything can click through. */
  pointer-events: none;
}
.section-header .gold-line,
.gold-line--center { margin-left: auto; margin-right: auto; }

.eyebrow {
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  color: var(--gold);
  margin-bottom: var(--space-sm);
  display: block;
}

.lede { font-size: 1.15rem; }

/* Spacing utilities. These exist so no template ever needs a style attribute:
   the CSP sets style-src 'self' with no 'unsafe-inline', which blocks inline
   style attributes outright. A missing utility class is a visual bug; adding
   'unsafe-inline' to get one would be a security regression. */
.mt-sm  { margin-top: var(--space-sm); }
.mt-lg  { margin-top: var(--space-lg); }
.mt-xl  { margin-top: var(--space-xl); }
.mt-2xl { margin-top: var(--space-2xl); }

/* Visible to a screen reader, not to the eye. Deliberately NOT display:none,
   which would hide it from assistive tech too. Takes no clicks, so it cannot
   sit invisibly on top of selectable copy. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Hero. The wash is decorative, so it is behind the text and takes no clicks.
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  color: var(--cream);
  padding: var(--space-4xl) 0 var(--space-3xl);
  border-bottom: 1px solid var(--gold-10);
  overflow: hidden;
}

.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 20% 0%, var(--undertow-60), transparent 65%);
  /* LOCKED: decorative layers never intercept a click. Removing either of these
     two lines makes every word in the hero unselectable. */
  pointer-events: none;
  z-index: 0;
}

.hero > * {
  position: relative;
  z-index: 1;
}

/* The rule sits tight under an h2 in a section header, which is the site's
   look. Under a 3.5rem h1 it needs air, or it reads as an underline on the
   first two words rather than as a rule. Only the PROFILE hero has one: the
   listing hero's accent line is its gold, and a rule as well is gold noise. */
.hero .gold-line { margin-top: var(--space-md); }

/* --------------------------------------------------------------------------
   The LISTING hero: centred, two-tone headline, search bar, city chips.

   Its own measure rather than .narrow. 780px is a reading measure for
   paragraphs; a 3.5rem Playfair headline needs a different one, and the lede
   under it needs a third. One box for all three is what produced a four-line
   lede under a three-line headline. The PROFILE hero is a portrait beside a
   headline and uses a bare .wrap, so none of this reaches it.
   -------------------------------------------------------------------------- */
.hero__inner {
  max-width: 46rem;
  margin: 0 auto;
  text-align: center;
}

/* Balanced, because the accent line is a hard break and everything ELSE in the
   headline still has to wrap sensibly around it. Unsupported browsers ignore
   balance, which is why the measure above is tuned to work on its own. */
.hero__title {
  margin-bottom: var(--space-md);
  text-wrap: balance;
}

/* The gold payoff line. Near Me's headline is a cream line over a gold line,
   and that single move is most of why it reads as the newer page. */
.hero__accent { color: var(--gold); }

/* The hero paragraph is a reading measure again, narrower than the headline. */
.hero .lede {
  max-width: var(--max-width-text);
  margin-left: auto;
  margin-right: auto;
}

/* The count sentence. It is the line an answer engine quotes to say how big
   this directory is, so it stays on the page; it is not the line a visitor came
   for, so it does not read at body size directly under the search bar. */
.hero__trust {
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--cream-60);
  max-width: var(--max-width-text);
  margin: var(--space-lg) auto 0;
}

/* "Popular" plus the six biggest cities. Real anchors to real city pages, so
   the highest-authority page in the section links its highest-value children
   from above the fold. */
.popular {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs) var(--space-sm);
  margin-top: var(--space-lg);
}

.popular__label {
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  color: var(--cream-60);
}

.popular .chips { justify-content: center; }

/* --------------------------------------------------------------------------
   Filter bar. A plain GET form: works with JavaScript disabled, works for a
   crawler, and every filtered view has a real URL an answer engine can cite.

   ONE JOINED CONTROL, NOT A PANEL OF FIVE FIELDS. It used to be a 263px
   bordered panel under its own centred section header, which is 450px of
   furniture between the hero and the first facilitator and reads as a form
   rather than as an invitation. Name, city, state and the button are now
   segments of a single glass bar in the hero: the inputs drop their own borders
   and background so the CONTAINER is the control, and the focus outline is what
   distinguishes a segment.

   GRID, NOT FLEX WRAP. A grid can pair City with State on a phone, which is
   what the mobile block below does, while desktop gets one row from the same
   rules. Flex with a basis gives each field its own row the moment the viewport
   is narrower than about 26rem, which on a phone is a tower the visitor has to
   scroll past to reach a single facilitator.
   -------------------------------------------------------------------------- */
.filters {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) 6rem auto;
  gap: var(--space-xs);
  align-items: center;
  background: var(--panel);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-md);
  padding: var(--space-xs);
  color: var(--cream);
  max-width: 44rem;
  margin: var(--space-xl) auto 0;
  text-align: left;
}

/* Inside the bar the segments are flat: the bar owns the border, the radius and
   the glass. A segment that kept its own 1px gold hairline would read as four
   stacked controls that happen to be touching. */
.filters .field input {
  background: transparent;
  border-color: transparent;
  /* The bar is only 44rem wide and the State segment is 6rem of it. At the
     shared 1rem side padding the placeholder clips; 0.75rem clears it with
     room to spare and keeps the four segments visually even. */
  padding-left: var(--space-sm);
  padding-right: var(--space-sm);
}
.filters .field input:focus { border-color: var(--gold); background: var(--undertow-60); }

/* The bar's labels are sr-only, so the PLACEHOLDER is the only thing a sighted
   visitor reads. cream-40 measured 2.9:1 against the bar, which is fine for
   decoration and not fine for the only naming of a control. */
.filters .field input::placeholder { color: var(--cream-60); }

/* The submit segment is a real button, not a bar segment: it keeps the gold. */
.filters .field--submit .btn { white-space: nowrap; padding: var(--space-sm) var(--space-lg); }

/* The two dropdowns, behind a native disclosure INSIDE the same form. A
   collapsed control is not a disabled control, so both axes still submit
   together, and <details> needs no JavaScript (this Worker ships none). */
.filters__more {
  grid-column: 1 / -1;
  text-align: center;
  border-top: 1px solid var(--gold-20);
  margin-top: var(--space-xs);
}

.filters__more summary {
  display: inline-block;
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--warm-sand);
  cursor: pointer;
  list-style: none;
  /* 0.55rem of vertical padding on a 0.9rem line box clears the 32px floor for
     a secondary control without making the closed bar look two-storey. */
  padding: 0.55rem var(--space-sm);
  border-radius: var(--radius-sm);
}
.filters__more summary::-webkit-details-marker { display: none; }
.filters__more summary::after {
  content: " +";
  color: var(--gold);
}
.filters__more[open] summary::after { content: " \2212"; }
.filters__more summary:hover { color: var(--gold); }

.filters__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: var(--space-md);
  align-items: end;
  padding: var(--space-xs) var(--space-sm) var(--space-sm);
  text-align: left;
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  /* Grid items default to min-content, and a <select> holding "Conscious
     Connected Breathwork" has a very wide min-content. Without this the track
     refuses to shrink and the whole page scrolls sideways on a phone. */
  min-width: 0;
}

/* The site's label: sand, small, medium weight, sentence case. The gold
   uppercase micro-caps this used to carry are the single cheapest thing that
   made the filter bar look like it came from somewhere else. */
.field label {
  display: block;
  font-size: var(--fs-small);
  font-weight: var(--fw-medium);
  color: var(--warm-sand);
  margin-bottom: var(--space-xs);
}

.field input,
.field select,
.field textarea {
  font-family: var(--font-body);
  /* 1rem minimum. Anything smaller and iOS Safari zooms the page on focus, which
     is the classic "the whole layout jumped" mobile bug. */
  font-size: var(--fs-body);
  color: var(--cream);
  background: var(--undertow-60);
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-md);
  padding: var(--space-sm) var(--space-md);
  width: 100%;
  min-width: 0;
  /* Comfortable touch target on a phone without making desktop fields chunky. */
  min-height: 2.75rem;
  transition: border-color var(--ease);
}

.field input:focus,
.field select:focus,
.field textarea:focus { outline: none; border-color: var(--gold); }

/* The submit button sits on the baseline of the last row of inputs. */
.field--submit .btn { width: 100%; }

.field textarea { min-height: 9rem; resize: vertical; }

.field input::placeholder,
.field textarea::placeholder { color: var(--cream-40); }

/* --------------------------------------------------------------------------
   Buttons. The site's, exactly: 4px radius, not a 20px pill. This was the
   loudest visual mismatch after the navigation.
   -------------------------------------------------------------------------- */
.btn,
.btn-primary {
  display: inline-block;
  background: var(--gold);
  color: var(--abyss);
  font-family: var(--font-body);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-btn);
  padding: var(--space-sm) 1.75rem;
  border-radius: var(--radius-sm);
  border: none;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: all var(--ease);
}
.btn:hover,
.btn-primary:hover {
  background: var(--warm-sand);
  color: var(--abyss);
  transform: translateY(-2px);
  box-shadow: var(--shadow-hover);
  text-decoration: none;
}

.btn--ghost,
.btn-ghost {
  display: inline-block;
  background: transparent;
  color: var(--gold);
  font-family: var(--font-body);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-btn);
  padding: var(--space-sm) 1.75rem;
  border-radius: var(--radius-sm);
  border: 2px solid var(--gold);
  cursor: pointer;
  text-decoration: none;
  transition: all var(--ease);
}
.btn--ghost:hover,
.btn-ghost:hover {
  background: var(--gold);
  color: var(--abyss);
  transform: none;
  text-decoration: none;
}

/* A ghost button on the sand section takes the sand treatment, but one inside a
   dark panel on that section keeps the gold. */
.section--alt .btn--ghost,
.section--alt .btn-ghost { color: var(--abyss); border-color: var(--abyss); }
.section--alt .btn--ghost:hover,
.section--alt .btn-ghost:hover { background: var(--abyss); color: var(--warm-sand); }
.section--alt :is(.card, .empty, .notice, .relay-form, .filters, .event-card) .btn--ghost {
  color: var(--gold);
  border-color: var(--gold);
}
/* The .section--alt trap, fourth sighting (see css-cards.ts): the rule above
   holds color at gold with three classes, so the two-class .btn--ghost:hover
   swaps the background to gold and loses the color swap, gold on gold. Four
   classes settles it, exactly as .card__view does. */
.section--alt .event-card .event-card__cta:hover,
.section--alt .event-card .event-card__cta:focus-visible {
  background: var(--gold);
  color: var(--abyss);
  text-decoration: none;
}

.btn-group { display: flex; gap: var(--space-lg); flex-wrap: wrap; }
.btn-group--center { justify-content: center; }

/* ==========================================================================
   Touch targets
   ==========================================================================

   Measured at 390px, 430px and 360px against the rendered pages: a city chip
   came out 34px tall, a specialty chip on a profile 36px, and the card title
   link 33px. The chip anchors were already carrying their pill's padding, but
   they were built to a 32px floor, and 32px is not the floor that matters. The
   iOS Human Interface Guidelines minimum is 44px, and on a listing page the
   card title is the ONLY way into a profile, so a 33px target is the difference
   between finding a facilitator and mis-tapping the one beside them.

   Gated on a pointer: coarse query rather than on a width, because the constraint is
   the fingertip and not the screen. A 1024px tablet needs the bigger target and
   a 390px window on a desktop does not, and a width query gets both backwards.
   Desktop pointer styling is left exactly as it was.

   min-height plus a centring flex, not extra padding: padding would also widen
   the pill and rewrap the chip rows. */
@media (pointer: coarse) {
  .chip > a,
  .chip:not(:has(> a)),
  .filters__more > summary {
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* The card title is an inline anchor inside a heading, so it cannot take a
     min-height on its own. The padding is vertical only and is offset by a
     negative margin, so the tap target grows without moving the text or
     changing the height of the card.

     BOTH selectors are required, and the second is the whole point. The base
     sheet sets this anchor's padding through a .section--alt compound selector,
     which is two classes. A media query adds NO specificity, so a bare
     one-class version here loses to it, and it loses on every card on the site
     because the grid always sits in a sand section. Measured: the rule appeared
     to do nothing and the target stayed at 33px. This is the same trap the
     comment above the .section--alt anchor rule already documents. */
  .card__name a,
  .section--alt .card__name a {
    display: inline-block;
    padding-block: 0.65rem;
    margin-block: -0.65rem;
  }
}

/* ==========================================================================
   Global site header. Identical structure to liquidbreathwork.com.
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--abyss);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--gold-10);
}

.site-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  /* Wider than the page container, exactly as the site's nav is. */
  max-width: var(--max-width-nav);
  margin: 0 auto;
  padding: 1.25rem 5%;
  width: 100%;
  gap: var(--space-md);
}

.site-logo {
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  font-size: 1.3rem;
  color: var(--warm-sand);
  letter-spacing: 1px;
  text-decoration: none;
  white-space: nowrap;
}
.site-logo:hover { color: var(--gold); text-decoration: none; }

.nav-links {
  display: flex;
  gap: 0;
  list-style: none;
  align-items: center;
  margin: 0;
  padding: 0;
}

.nav-links > li { position: relative; }

.nav-links > li > a,
.nav-links > li > .nav-dropdown-trigger {
  display: block;
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-nav);
  color: var(--cream);
  text-decoration: none;
  transition: color var(--ease);
  padding: 0.5rem 1rem;
  cursor: pointer;
  background: none;
  border: none;
  white-space: nowrap;
  /* The site's trigger is a <button>, which takes the UA's line-height: normal.
     Ours is a <label> (the CSS-only drawer needs one), and a label inherits the
     body's 1.8. That single inherited value made every mobile drawer row about
     8px taller than the live site's and the desktop header 1.4px taller. Same
     element, same class names, different default. */
  line-height: normal;
}

.nav-links > li > a:hover,
.nav-links > li > .nav-dropdown-trigger:hover { color: var(--gold); text-decoration: none; }


.nav-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 240px;
  background: var(--abyss);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--gold-10);
  border-radius: var(--radius-md);
  padding: var(--space-xs) 0;
  margin: 0;
  list-style: none;
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: all var(--ease);
  z-index: 200;
}

/* ONE PANEL AT A TIME, WITH NO SCRIPT.
   The site pins a panel with main.js and li.dropdown-open; here the pin is the
   checkbox, and this rule is the pinned state. It is deliberately in its OWN
   rule, separate from the :has() guard below: a browser without :has() drops
   that rule as invalid, and this one still works, so the worst case is the
   older hover-anywhere behaviour rather than a menu that will not open. */
.nav-links > li > .nav-state:checked ~ .nav-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* Hover is the site's rule, with the site's guard: a panel may open on hover or
   keyboard focus ONLY while nothing is pinned. While any menu's checkbox is
   checked, hovering a neighbour does nothing, so two panels can never show.
   (:focus-within is additive; the live site does not have it.)

   The :focus-within arm carries a SECOND guard, and it is load-bearing. Clicking
   a label focuses its checkbox, so after the scrim unpins a menu that menu's own
   checkbox still holds focus, :focus-within stayed true, and the panel that was
   just closed re-opened while the pointer opened a second one somewhere else.
   Measured: two panels visible at once. Excluding the item's own checkbox keeps
   the rule doing the thing it is for, which is holding a panel open while the
   keyboard walks THROUGH it. This is the CSS-only stand-in for the site's
   nav-focus-off class. */
.nav-links:not(:has(> li > .nav-state:checked)) > li:hover > .nav-dropdown,
.nav-links:not(:has(> li > .nav-state:checked))
  > li:focus-within:not(:has(> .nav-state:focus))
  > .nav-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* The outside-click closer. A viewport-sized label for the pinned checkbox,
   painted nothing, present only while that checkbox is checked. It sits above
   the header's own links and below the open panel, so a click anywhere except
   inside the panel unpins. This is also what makes "never two panels" true even
   without :has(): another menu's chevron is behind the scrim, so the click that
   would have opened it closes this one instead.
   Hidden on mobile (see the <= 768px block): the drawer is a stacked accordion,
   and a scrim there would swallow every other row. */
.nav-scrim { display: none; }
.nav-links > li > .nav-state:checked ~ .nav-scrim {
  display: block;
  /* NOT inset: 0. The header runs a backdrop-filter, and a filtered element is
     the containing block for its fixed-position descendants, so inset: 0 sized
     this to the HEADER and an outside click below the menu went straight through
     to the page (measured: the panel stayed pinned). The header is sticky at
     top: 0, so anchoring to its top and taking a full viewport height covers the
     screen. right: 0 rather than 100vw, so a scrollbar cannot make it overflow. */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 100vh;
  z-index: 150;
  background: none;
  cursor: default;
}

/* Linked menu (Classes, Training). The label is a real <a> to the hub page and
   the chevron beside it is the toggle, so the label passes link weight and stays
   tappable on a phone. Site rule, verbatim geometry. */
.nav-links > li.nav-dropdown-linked {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.nav-links > li.nav-dropdown-linked > .nav-dropdown-label {
  display: block;
  font-family: var(--font-body);
  font-weight: var(--fw-regular);
  font-size: var(--fs-nav);
  color: var(--cream);
  text-decoration: none;
  transition: color var(--ease);
  padding: 0.5rem 0.3rem 0.5rem 1rem;
  white-space: nowrap;
  line-height: normal;
}
.nav-links > li.nav-dropdown-linked > .nav-dropdown-label:hover {
  color: var(--gold);
  text-decoration: none;
}
.nav-links > li.nav-dropdown-linked > .nav-dropdown-trigger {
  padding-left: 0;
  padding-right: 1rem;
  font-size: 0.8em;
  line-height: 1;
}

/* ==== MEGA MENU (desktop) ====
   Davis Tent style (Ed Grace, 2026-07-23), generated from the group and desc
   fields in includes/nav.json. The panel spans the header, so its parent li goes
   static and the sticky header becomes the containing block. */
.nav-links > li:has(> .nav-mega) { position: static; }
.nav-mega {
  left: 0;
  right: 0;
  min-width: 0;
  padding: 1.75rem 5% 2rem;
  display: flex;
  gap: 2.5rem;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  border-top: 1px solid var(--gold-20);
}
/* Hover bridge: the header's bottom padding sits between the label and the
   panel. This strip belongs to the panel, so the pointer never leaves the menu
   while crossing it, and it is inert while the panel is hidden. */
.nav-mega::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -1.5rem;
  height: 1.5rem;
}
.nav-mega__group {
  flex: 1 1 0;
  min-width: 0;
  list-style: none;
}
.nav-mega__group--wide { flex: 2 1 0; }
.nav-mega__heading {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
  padding: 0 1rem 0.6rem;
  margin-bottom: 0.4rem;
  border-bottom: 1px solid var(--gold-10);
}
.nav-mega__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.15rem;
}
.nav-mega__group--wide .nav-mega__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.nav-mega a,
.nav-dropdown--rich a { border-radius: var(--radius-sm); }
.nav-dropdown--rich { min-width: 300px; }
.nav-item__label {
  display: block;
  color: var(--cream);
  font-weight: var(--fw-medium);
  line-height: 1.3;
}
.nav-item__desc {
  display: block;
  color: var(--cream-60);
  font-size: var(--fs-xs);
  line-height: 1.4;
  margin-top: 0.15rem;
}
.nav-dropdown a:hover .nav-item__label { color: var(--gold); }

.nav-dropdown a {
  display: block;
  padding: 0.6rem 1.25rem;
  color: var(--cream);
  font-size: var(--fs-small);
  text-decoration: none;
  transition: all var(--ease);
}
.nav-dropdown a:hover {
  color: var(--gold);
  background: var(--undertow-60);
  text-decoration: none;
}


/* The gold CTA pill. The site wins this with !important over its own nav-link
   rules; the same is needed here for the same reason. */
.nav-links > li > a.nav-cta,
a.nav-cta {
  background: var(--gold) !important;
  color: var(--abyss) !important;
  padding: 0.5rem 1.25rem !important;
  font-size: var(--fs-small) !important;
  font-weight: var(--fw-semibold) !important;
  border-radius: var(--radius-sm);
  letter-spacing: 0.02em;
  transition: opacity var(--ease), transform var(--ease);
}
.nav-links > li > a.nav-cta:hover,
a.nav-cta:hover {
  opacity: 0.9;
  color: var(--abyss) !important;
  transform: translateY(-1px);
  text-decoration: none;
}

/* --------------------------------------------------------------------------
   The hamburger, and the checkbox that drives it.

   CLIPPED, NOT display:none. A display:none input is not focusable, which would
   leave a keyboard user with no way to open the drawer on a phone. Clipped, it
   is still in the focus order: Tab reaches it, Space opens the menu.
   -------------------------------------------------------------------------- */
.nav-state {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  /* It is 1px and invisible, but the locked rule is that nothing invisible ever
     intercepts a pointer. The label is what a pointer uses. */
  pointer-events: none;
}

.nav-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 8px;
  /* A 3-bar icon is 34px tall inside 8px of padding: already past the 44px
     touch floor once the padding is counted. */
}
.nav-toggle span {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--cream);
  margin: 5px 0;
  transition: all var(--ease);
}

/* Keyboard focus lands on the clipped input, so the visible affordance has to
   show it. Same for the dropdown triggers. */
.nav-state:focus-visible + .nav-toggle span { background: var(--gold); }
.nav-state:focus-visible + .nav-dropdown-trigger {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  color: var(--gold);
}

/* ==========================================================================
   Global site footer.
   ========================================================================== */

.site-footer {
  background: var(--abyss);
  border-top: 1px solid var(--gold-10);
  padding: var(--space-2xl) 5%;
}

.footer-grid {
  max-width: var(--max-width);
  margin: 0 auto var(--space-lg);
  display: grid;
  grid-template-columns: 1.4fr repeat(5, 1fr);
  gap: var(--space-lg);
}

.footer-brand .site-logo { display: block; margin-bottom: var(--space-md); }
.footer-brand p {
  color: var(--cream-50);
  font-size: var(--fs-nav);
  line-height: 1.7;
}

.site-footer h4 {
  color: var(--warm-sand);
  font-family: var(--font-body);
  font-weight: var(--fw-semibold);
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: var(--space-md);
}

.site-footer a {
  color: var(--cream-50);
  font-size: var(--fs-nav);
  display: block;
  margin-bottom: var(--space-xs);
  transition: color var(--ease);
  text-decoration: none;
}
.site-footer a:hover { color: var(--gold); text-decoration: none; }

.social-links {
  display: flex;
  gap: var(--space-lg);
  margin-top: var(--space-md);
}
.social-links a {
  width: 40px;
  height: 40px;
  margin-bottom: 0;
  border: 1px solid var(--gold);
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--ease);
  font-size: var(--fs-small);
}
.social-links a:hover { background: var(--gold); color: var(--abyss); }

/* The site ships these two rules in an inline <style> inside the footer.
   style-src 'self' has no 'unsafe-inline', so an inline block would be
   dropped: they live in the stylesheet instead. */
.footer-quicklinks-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.3rem 0.9rem;
}
.footer-quicklinks-grid a {
  font-size: 0.72rem;
  line-height: 1.3;
  white-space: normal;
  margin-bottom: 0;
}

.footer-bottom {
  border-top: 1px solid var(--gold-10);
  padding-top: var(--space-xl);
  text-align: center;
}
.footer-bottom p {
  color: var(--warm-sand);
  font-size: var(--fs-small);
  opacity: 0.6;
  margin-bottom: 0;
}
.footer-legal { margin-top: var(--space-xs); }
.footer-legal a {
  display: inline;
  margin-bottom: 0;
  color: var(--warm-sand);
  font-size: inherit;
}
.footer-legal a:hover { color: var(--gold); }

/* ==========================================================================
   Chrome responsive. The site's breakpoints, not the directory's.
   ========================================================================== */

@media (max-width: 1024px) {
  /* The site has NO rule between 481px and 1024px, so its six-column footer
     squeezes on a tablet. Reproducing that bug is not parity, it is a bug, so
     the grid drops to three columns here and one below 480px. Flagged in
     SITE-PARITY.md section 4g. */
  .footer-grid { grid-template-columns: repeat(3, 1fr); }
  .footer-quicklinks-col { grid-column: span 2; }
}

@media (max-width: 768px) {
  .nav-toggle { display: block; }

  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--abyss);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    flex-direction: column;
    padding: var(--space-xl) 5%;
    gap: 0;
    border-top: 1px solid var(--gold-10);
    /* The drawer is taller than a phone screen once a sub-menu is open, and it
       is absolutely positioned, so it has to scroll inside itself rather than
       running off the bottom of the viewport with no way to reach the end. */
    max-height: 80vh;
    overflow-y: auto;
  }

  /* The whole mobile navigation, with no JavaScript: the label checks the box,
     the box opens the drawer. The site does this with a class main.js adds. */
  .nav-state--drawer:checked ~ .nav-links { display: flex; }

  .nav-links > li { width: 100%; text-align: center; }

  .nav-links > li > a,
  .nav-links > li > .nav-dropdown-trigger { padding: 0.75rem 0; width: 100%; }

  .nav-dropdown {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    background: none;
    border: none;
    padding: 0 0 0 var(--space-md);
    display: none;
    min-width: 0;
  }
  /* Same trick one level down, for each sub-menu inside the drawer. */
  .nav-links > li > .nav-state:checked ~ .nav-dropdown { display: block; }
  /* Sub-items read LEFT-aligned inside a centred 240px box on the live drawer.
     The text-align: center on li above cascades into the sub-menu and made ours
     centred and full width, which is the one geometry difference a person would
     actually notice with a drawer section open.
     (No backticks in this file: it is one big template literal.) */
  .nav-dropdown {
    text-align: start;
    width: 240px;
    margin-inline: auto;
  }
  .nav-dropdown a { padding: 0.5rem 0; font-size: var(--fs-small); }

  /* The scrim is a desktop affordance. In the stacked drawer it would cover
     every other row, so a second tap could never reach another section. */
  .nav-scrim { display: none !important; }

  /* Linked menu in the drawer. The label and its chevron stay centred as one
     group, like every other row, and the panel wraps onto its own line beneath.
     line-height is pinned so this row is the same height as the rows next to it. */
  .nav-links > li.nav-dropdown-linked { justify-content: center; }
  .nav-links > li.nav-dropdown-linked > .nav-dropdown-label {
    width: auto;
    flex: 0 0 auto;
    line-height: 1.15;
    padding: 0.75rem 0;
  }
  .nav-links > li.nav-dropdown-linked > .nav-dropdown-trigger {
    width: auto;
    flex: 0 0 auto;
    padding: 0.75rem 0.5rem 0.75rem 1rem;
    min-height: 44px;
  }
  .nav-links > li.nav-dropdown-linked > .nav-dropdown { flex-basis: 100%; }

  /* Mega menu collapses into the accordion: columns stack, headings become small
     labels, descriptions are dropped so the drawer does not run off the phone. */
  .nav-mega {
    display: none;
    position: static;
    padding: 0 0 0 var(--space-md);
    border-top: none;
    border-radius: 0;
    gap: 0;
  }
  .nav-links > li > .nav-state:checked ~ .nav-mega { display: block; }
  .nav-mega::before { content: none; }
  .nav-mega__group { margin-top: 0.35rem; }
  .nav-mega__heading { padding: 0.35rem 0; border-bottom: none; margin-bottom: 0; }
  .nav-mega__list,
  .nav-mega__group--wide .nav-mega__list { display: block; }
  .nav-item__desc { display: none; }
  .nav-item__label { font-weight: var(--fw-regular); }
  .nav-dropdown--rich { min-width: 0; }

  .site-nav { padding: var(--space-md) 5%; }

  .footer-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-quicklinks-col { grid-column: span 2; }
  .footer-quicklinks-grid a { font-size: var(--fs-small); }
}

@media (max-width: 480px) {
  .footer-grid { grid-template-columns: 1fr; gap: var(--space-lg); }
  .footer-quicklinks-col { grid-column: auto; }
  .footer-quicklinks-grid { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   The results bar. Heading left, one affordance right, grid immediately under.

   This is the Near Me pattern, and it replaces a centred eyebrow + h2 + gold
   rule that cost about 140px and announced a SEARCH rather than a RESULT. The
   centred section header is still the site's rhythm everywhere it introduces
   editorial copy; it is the wrong component directly above a grid of people.
   -------------------------------------------------------------------------- */
.results-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
  margin-bottom: var(--space-lg);
}

.results-head h2 { margin: 0; font-size: var(--fs-h3); }

.results-head__sub {
  margin: var(--space-xs) 0 0;
  font-size: var(--fs-small);
  color: var(--cream-80);
}

.results-head__more {
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
  /* Padding, not a bigger font: a 20px line box is under the tap-target floor
     for a control that is the only way out of a filtered view. */
  padding: 0.5rem 0;
}

.results-head__arrow {
  display: inline-block;
  margin-left: 0.35rem;
  transition: transform var(--ease);
}
.results-head__more:hover .results-head__arrow { transform: translateX(3px); }

/* On sand, the results heading and its subline are the section's own text and
   must take the section's colour, not a panel's. */
.section--alt .results-head h2 { color: var(--abyss); }
.section--alt .results-head__sub { color: var(--undertow); }

/* --------------------------------------------------------------------------
   Cards. The site's glass card: --panel, 1px gold-20 hairline, 8px radius,
   and a 4px lift on hover.

   TIGHTER THAN IT WAS. At 2rem of padding, a 4.5rem portrait and 2rem grid
   gaps, a card measured 385px tall to carry a name, one meta line, two lines of
   excerpt and two chips. Near Me's card is 373px WITH a photograph in it. The
   padding, the portrait and the gaps each came down one step on the same scale,
   which is the whole "tighter and more scannable" change: no information left
   the card.
   -------------------------------------------------------------------------- */
.card-grid {
  display: grid;
  /* 17rem, not 19rem: three columns on a 1280px screen instead of two, which is
     a third fewer rows to scroll through at any directory size. auto-FILL, not
     auto-fit, so the tracks stay put and a short row is not stretched. */
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  /* Cards in a row share a height, and the chip row is pushed to the bottom of
     each one, so the specialties line up across the row exactly as Near Me's
     price line does. This used to be align-items: start, on the grounds that
     stretching left a short card carrying 200px of empty navy. That was true
     when the excerpt could run to five lines: the range across twelve cards was
     202px to 397px. Clamping the EXCERPT to two lines cuts the range to about
     95px, which is slack a bottom-aligned chip row absorbs.
     The NAME is deliberately not clamped. A clamp on a person's name hides part
     of their name to make a card tidier, which is a worse trade than a taller
     card, and grid rows share a height anyway so one long name costs its row and
     nothing else. It gets text-wrap: balance instead, so a name that does need
     two or three lines breaks into even ones
     instead of a hole the eye reads as a rendering fault. */
  gap: var(--space-lg);
  margin-top: 0;
  padding: 0;
  list-style: none;
}

/* A city with one facilitator, or two, is a real and common state, and the
   auto-fill grid leaves that card marooned in the left third of a 1200px sand
   band. One or two results get a centred layout at a card's natural width
   instead: the same component, laid out deliberately rather than by accident. */
.card-grid:has(> li:first-child:last-child) {
  grid-template-columns: minmax(0, 24rem);
  justify-content: center;
}
.card-grid:has(> li:first-child + li:last-child) {
  grid-template-columns: repeat(2, minmax(0, 24rem));
  justify-content: center;
}

/* The :has() rules above outrank the generic mobile .card-grid override on
   specificity, whatever the source order, so without this block a
   two-facilitator city page kept two ~163px columns at 390px and overflowed
   the viewport by 6px at 360px. Measured, not guessed. One centred column. */
@media (max-width: 640px) {
  .card-grid:has(> li:first-child:last-child),
  .card-grid:has(> li:first-child + li:last-child) {
    grid-template-columns: minmax(0, 24rem);
  }
}
.card {
  /* The containing block for the stretched name link below. */
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  background: var(--panel);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--cream);
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  box-shadow: var(--shadow-card);
  transition: all var(--ease);
}
.card:hover {
  border-color: var(--gold-40);
  transform: translateY(-4px);
  box-shadow: var(--shadow-hover);
}

/* Portrait ABOVE the name and location, and this reverses what used to be here.

   The old rule put a 60px circle BESIDE the name, on the grounds that it halved
   the vertical space the identity block costs. That trade only holds while the
   portrait is a thumbnail. Ryan, 2026-08-12: "make the pictures bigger", and on
   a directory of Breathwork facilitators the face is the card's subject, not a
   decoration next to the subject.

   MEASURED, WHICH IS WHY IT HAD TO STACK. At 1280px the grid is three tracks
   and a card is 344px wide, so 24px of padding each side leaves 296px. Beside a
   144px circle plus a 16px gap that is a 136px column for a name, and "Shelby
   Von Oepen" at fs-h4 does not fit in 136px on fewer than three lines. Stacked,
   the name gets the full 296px and wraps less than it did at 60px.

   The cost is about 165px of card height. That is the trade Ryan asked for. */
.card__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  /* Left, not centred: everything under it (excerpt, chips, View profile) is
     left-aligned, and a centred name over a left-aligned excerpt reads as two
     cards stacked. */
  align-items: flex-start;
}

/* Full card width, explicitly. The head is a column flex container with
   align-items: flex-start, which would otherwise shrink-wrap this block and let
   the name's wrap point drift with the length of the location line. */
.card__id { min-width: 0; align-self: stretch; }

.card__nameline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem var(--space-xs);
}

.card__name {
  margin: 0;
  font-size: var(--fs-h4);
  /* Even lines when a long name has to wrap. Unsupported browsers ignore it and
     get the ordinary greedy wrap, which is what shipped before. */
  text-wrap: balance;
  overflow-wrap: break-word;
  /* 1.2 is the heading line-height token, but a card title is one or two words
     on one or two lines, and 1.2 on a 1.25rem Playfair name leaves the second
     line touching the first. 1.25 is the smallest value that clears the
     descenders on a name like "Raghunathan". */
  line-height: 1.25;
  color: var(--cream);
}

.card__meta {
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--cream-80);
  margin: var(--space-xs) 0 0;
}

/* Two lines, hard.
   The excerpt is already cut to 120 characters server-side, but 120 characters
   is two lines on a 337px card and five on a 260px one, which is where most of
   the height variance in the grid came from. The clamp is VISUAL only: the full
   excerpt stays in the DOM for a crawler and for an answer engine, and the full
   bio is one click away on the profile. */
.card__excerpt {
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--cream-80);
  margin: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* The specialty row sits on the floor of the card, so it lines up across a row
   whatever the name and the excerpt did above it. */
.card > .chips {
  margin-top: auto;
  padding-top: var(--space-xs);
}

/* Card chips are metadata, not controls. A tighter pill fits "Sound Healing"
   and "Retreats" on one line inside a 337px card, where the browse chips'
   0.85rem side padding pushed every label onto a row of its own. */
.card .chip {
  letter-spacing: 0.02em;
  padding: 0.3rem 0.65rem;
}

/* --------------------------------------------------------------------------
   The whole card is a click target.

   The Bootstrap stretched-link pattern: the card is position: relative, the
   name anchor's ::after fills it, and the click lands on the profile from
   anywhere on the card. This overlay is FUNCTIONAL, not decorative, so it is
   the one full-bleed pseudo-element in this sheet that keeps its clicks
   (facilitator-public-render.test.ts carves out exactly this selector).

   The selection rule still holds, mechanically: every text layer and the
   explicit "View profile" link sit at z-index 2, above the overlay at 1, so
   dragging across the excerpt selects words rather than starting a link drag,
   and elementFromPoint over any text still returns the text element. What the
   overlay owns is the card's EMPTY space: padding, gaps, the portrait, the
   meta area between the lines. Nothing here disables selection or clicks.
   -------------------------------------------------------------------------- */
.card__name a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.card__meta,
.card__excerpt,
.card > .chips,
.card__view {
  position: relative;
  z-index: 2;
}

/* The affordance the stretched link cannot provide: a visible "View profile"
   at the foot of every card. It says where the card goes; the stretched link
   makes everywhere on the card go there. Flex stretches it full width, which
   is a bigger target and a straighter row across the grid. */
.card__view {
  margin-top: auto;
}
.card > .chips + .card__view { margin-top: var(--space-sm); }

/* The .section--alt trap, third sighting (see css-base.ts): inside a card on
   sand, ".section--alt :is(.card, ...) .btn--ghost" holds color at gold with
   three classes of specificity, so the base .btn--ghost:hover (two classes)
   swaps the background to gold and LOSES the color swap: gold on gold, an
   invisible label. And ".section--alt .card a:hover" underlines it. Four
   classes settles both. */
.section--alt .card .card__view:hover,
.section--alt .card .card__view:focus-visible {
  background: var(--gold);
  color: var(--abyss);
  text-decoration: none;
}

@media (pointer: coarse) {
  .card__view {
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

/* --------------------------------------------------------------------------
   Breadcrumbs. The Near Me pattern: one quiet line above the profile hero,
   directory first, current page named and unlinked. The hero is always navy,
   so the muted cream and the gold link both sit on the site's own surface.
   -------------------------------------------------------------------------- */
.crumbs {
  margin-bottom: var(--space-lg);
  font-size: var(--fs-small);
}

.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  color: var(--cream-60);
}

/* The separator is drawn, not typed, so the markup stays a plain list of
   crumbs. The empty alternative text marks it decorative for screen readers
   that support the syntax; the ones that do not read one short slash. Inline
   and zero-size beyond its glyph: it covers nothing and takes no clicks. */
.crumbs li + li::before {
  content: "/";
  /* Engines without the alt-text syntax drop this line and keep the plain
     slash above; engines with it mark the glyph decorative. */
  content: "/" / "";
  color: var(--cream-40);
  margin-right: var(--space-xs);
}

/* The link is a real tap target on a small line: padding grows the box, the
   negative margin keeps the line's rhythm. */
.crumbs a {
  display: inline-block;
  padding: 0.5rem 0;
  margin: -0.5rem 0;
}

/* ── The one profile video (0114) ──────────────────────────────────────────
   A wrapper with an aspect-ratio rather than width/height on the iframe. The
   frame is 100% wide, so its attributes cannot reserve the right height, and
   without a reserved height the whole page below it jumps when a lazy frame
   finally loads. The ratio is 16/9 because both YouTube and Vimeo letterbox
   anything else into it themselves.

   aspect-ratio has a padding-top fallback underneath it for older engines: the
   modern rule wins wherever it is understood, and where it is not, the 56.25%
   padding box does the same job. */
.video-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--panel);
}
@supports not (aspect-ratio: 16 / 9) {
  .video-frame { height: 0; padding-top: 56.25%; }
}
.video-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
/* The self-hosted player (0115) fills the same aspect box the iframe does, so
   the two kinds of video occupy identical pixels and nothing below them moves
   depending on where a facilitator's video lives. */
.video-frame video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: var(--panel);
}

/* "About this video" (0115): quiet body text under the player. The summary is
   the disclosure's own affordance; everything else reads like the bio. */
.video-about {
  margin-top: var(--space-md);
}
.video-about summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--gold);
}
.video-about p {
  margin: var(--space-sm) 0 0;
}

/* Past classes (0115 workstream B): a dated, deliberately modest list. */
.past-events {
  list-style: none;
  margin: var(--space-md) 0 0;
  padding: 0;
}
.past-events li {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  align-items: baseline;
  padding: 10px 0;
  border-top: 1px solid var(--gold-20);
}
.past-events li:first-child {
  border-top: 0;
}
.past-events__date {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  color: var(--muted, inherit);
  min-width: 9.5rem;
}
.past-events__title {
  font-weight: 600;
}
.past-events__where {
  opacity: 0.75;
}


/* --------------------------------------------------------------------------
   Portrait, with a monogram underneath.

   The site already ships this pattern as .facilitator-photo: a circle with a
   3px gold border that scales on hover. This is that treatment, on a frame that
   also has to survive a missing photo and a dead one.

   THE FRAME OWNS THE SIZE, THE IMAGE ONLY FILLS IT. A facilitator with a photo,
   one without, and one whose photo 404s all occupy the same pixels, so nothing
   in the grid moves when an image loads or fails.

   THE MONOGRAM IS ALWAYS PAINTED, BEHIND. There is no CSS selector for a broken
   image and this Worker ships no JavaScript, so the only fallback that can
   survive a 404 is one that was already there.

   A BROKEN PHOTO PAINTS THE MONOGRAM, NOT A GLYPH. Chrome draws its
   broken-image icon inside any sized <img> that fails, even with alt="". A
   broken image is no longer a REPLACED element, though, which is the one moment
   CSS gets to generate a box inside it, so ::before repaints the monogram over
   the icon. A loaded image generates no pseudo-element and is untouched. This is
   not a theoretical edge case: a replacement photo deletes the live object
   before the replacement is approved, so a dead photo_key is a routine state.
   -------------------------------------------------------------------------- */
.portrait {
  position: relative;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: var(--radius-full);
  background: var(--undertow);
  border: 2px solid var(--gold);
  transition: transform 0.4s ease, border-color 0.4s ease;
}

/* Ryan, 2026-08-12: "make the pictures bigger." Card 3.75rem to 9rem, hero
   11rem to 15rem. This is a directory of PEOPLE, and at 60px the face was a
   thumbnail beside the name rather than the thing the card is about; the card
   now leads with it (see .card__head in css-cards.ts).

   15rem IS THE CEILING THE SOURCES SET. cover scales the SHORT side of a photo
   to the frame, and the six published photos' short sides measure 506, 553,
   675, 700, 800 and 800 pixels. 506 is exactly 2x a 240px hero, so the smallest
   photo on the directory still has a full retina budget. Past 15rem the
   narrowest source is being resampled upward, which is what "soft" looks like.
   The monogram sizes below move with the frames: a 1.3rem monogram in a 144px
   circle is a speck, and the initials fallback has to read as deliberate at the
   new size rather than as a photo that failed. */
.portrait--card { width: 9rem; height: 9rem; }
.portrait--hero { width: 15rem; height: 15rem; border-width: 3px; }

.portrait--hero:hover { transform: scale(1.05); border-color: var(--warm-sand); }

.portrait__monogram {
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  line-height: 1;
  color: var(--gold);
  /* Decorative: aria-hidden in the markup, since the name is the adjacent
     heading. Takes no clicks so it can never sit on top of selectable copy, and
     selection is never disabled here: that declaration is banned outright. */
  pointer-events: none;
}

.portrait--card .portrait__monogram { font-size: 3rem; }
.portrait--hero .portrait__monogram { font-size: 4.75rem; }

.portrait__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* NOT "center 0%", which is what shipped while the frames were small enough
     that the crop hardly mattered. At 144px and 240px it matters.

     These are not headshots. They are phone photos of real sessions, and MEASURED
     across the six published ones the subject's head sits between 20% and 35%
     down the frame, with the tall ones (553x1200, 700x926, 800x1066) carrying
     headroom, ceiling or a letterbox bar above it. A square crop pinned to the
     TOP of a 553x1200 photo spends its first 110px on that black bar.

     15% pushes the crop window down past the dead space and still stops well
     short of the eyes on every one of them, so nobody is beheaded and nobody
     gets a ceiling. The horizontal stays centred: the landscape sources put
     their subject near the middle, and cover only overflows one axis at a time
     anyway. Per-facilitator tuning (the way /get-certified carries "center 24%"
     and "58% 33%" inline) would need a column this projection does not have. */
  object-position: center 15%;
}

/* Generated ONLY when the image failed to load. Decorative, so it takes no
   clicks, exactly like every other overlay in this file. */
.portrait__img::before {
  content: attr(data-initials);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--undertow);
  color: var(--gold);
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  line-height: 1;
  pointer-events: none;
}

.portrait--card .portrait__img::before { font-size: 3rem; }
.portrait--hero .portrait__img::before { font-size: 4.75rem; }

/* --------------------------------------------------------------------------
   Chips: specialties, credentials.
   -------------------------------------------------------------------------- */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* A chip row that is a browse control rather than a card's metadata: it sits
   under a centred section header, so it centres too. */
.chips--center { justify-content: center; margin-top: var(--space-lg); }

.chip {
  display: inline-block;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--gold-40);
  background: var(--gold-10);
  color: var(--cream);
  transition: all var(--ease);
}

/* A chip that is a link gives its padding to the anchor, so the whole pill is
   the tap target instead of just the glyphs inside it. Without this the target
   is the ~19px text box, well under the 32px floor for a secondary control. */
.chip:has(> a) { padding: 0; }
.chip > a {
  display: block;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill);
  color: var(--cream);
  text-decoration: none;
}
.chip:hover { border-color: var(--gold); }

/* A chip inside a dark PANEL keeps the dark-surface treatment. The sand rule
   below repaints a chip deep-water, which on a deep-water card is an invisible
   pill: the specialty labels read as bare text with a stray indent. */
.section--alt .card .chip,
.section--alt .empty .chip,
.section--alt .notice .chip {
  background: var(--gold-10);
  border-color: var(--gold-40);
  color: var(--cream);
}

/* Locked contrast rule: a component sitting on the sand section must contrast
   with it. The navy version of a chip is cream text on a faint gold wash, which
   on sand is cream on sand and unreadable. On sand a chip becomes what every
   other component becomes: a solid deep-water panel with cream text. */
.section--alt .chip {
  background: var(--deep-water);
  border-color: var(--deep-water);
  color: var(--cream);
}
.section--alt .chip a {
  color: var(--cream);
  text-decoration: underline;
}

/* "+3 more" on a card. Quieter than a real specialty: it is a count, not a claim. */
.chip--count {
  border-style: dashed;
  background: transparent;
  color: var(--cream-80);
}

/* --------------------------------------------------------------------------
   Lists that breathe. Each item is its own card, not a cramped bullet.
   -------------------------------------------------------------------------- */
.panel-list {
  list-style: none;
  margin: var(--space-xl) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
  gap: var(--space-lg);
}

.panel-list > li {
  background: var(--panel);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--cream);
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-xl);
  transition: all var(--ease);
}
.panel-list > li:hover {
  border-color: var(--gold-40);
  transform: translateY(-4px);
}

.panel-list h3 {
  margin: 0 0 var(--space-xs);
  font-size: var(--fs-h4);
  color: var(--warm-sand);
}
.panel-list h3 a { display: inline-block; padding: 0.25rem 0; }
.panel-list p { margin: 0; color: var(--cream-80); font-size: var(--fs-small); }
.panel-list a { color: var(--gold); }

/* --------------------------------------------------------------------------
   Profile page
   -------------------------------------------------------------------------- */
.profile-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xl);
  align-items: flex-start;
}

.profile-headline { flex: 1 1 20rem; min-width: 15rem; }
.profile-headline h1 { margin-bottom: 0; }

/* The name line. It carried the name and the Verified pill on one baseline
   until that stamp came off on 2026-08-12, and it now carries the name and the
   certification stamp (see css-badge.ts): the wrapper is what puts a second
   element beside the name on the baseline rather than under it. */
.profile-nameline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-sm);
}

.profile-location {
  font-size: 1.1rem;
  color: var(--warm-sand);
  margin: 0 0 var(--space-xs);
}

.profile-credential { margin-bottom: 0; }

/* The skills follow the actions now, so they need the air the actions used to
   take from the block above them. */
.profile-headline .skills { margin-top: var(--space-xl); }

/* --------------------------------------------------------------------------
   The two skill axes in the profile hero.

   Two labelled groups, not one long chip row: "Specialties" is what they do in
   a Breathwork room, "Also trained in" is everything else they hold, and a
   visitor should never have to work out which a chip belongs to.

   The overflow disclosure is a native <details>. No JavaScript exists on these
   pages, and its contents are in the DOM at parse time, so every chip is
   readable by a crawler whether or not a human expands it.
   -------------------------------------------------------------------------- */
.skills {
  display: grid;
  gap: var(--space-lg);
  margin-top: var(--space-lg);
}

.skills__label {
  font-family: var(--font-body);
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  color: var(--gold);
  margin: 0 0 var(--space-xs);
}

.skills__more { margin-top: var(--space-xs); }

.skills__more summary {
  display: inline-block;
  font-size: var(--fs-xs);
  letter-spacing: 0.06em;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill);
  border: 1px dashed var(--gold-40);
  color: var(--gold);
  cursor: pointer;
  list-style: none;
}
.skills__more summary::-webkit-details-marker { display: none; }
.skills__more[open] summary { border-style: solid; }

/* Required beside the second axis, every time it renders: the credential line a
   few lines above covers a Breathwork certification and nothing else. */
.skills__note {
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: var(--cream-60);
  margin: var(--space-sm) 0 0;
  max-width: var(--max-width-text);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  margin-top: var(--space-xl);
}

/* --------------------------------------------------------------------------
   Pagination. Real anchors: a crawler walks the whole directory through them
   and a visitor with scripting off gets the identical experience.
   -------------------------------------------------------------------------- */
.pagination { margin-top: var(--space-2xl); }

.pagination__status {
  font-size: var(--fs-small);
  margin: 0 0 var(--space-md);
}

.pagination__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  list-style: none;
  margin: 0;
  padding: 0;
}

.pagination__page,
.pagination__step {
  display: inline-block;
  /* 2.75rem square: a comfortable thumb target, not a 12px numeral. */
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0.7rem var(--space-md);
  text-align: center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--gold-20);
  background: var(--panel);
  color: var(--cream);
  font-size: var(--fs-small);
  transition: all var(--ease);
}
.pagination__page:hover,
.pagination__step:hover { border-color: var(--gold); }

.pagination__page--current {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--abyss);
  font-weight: var(--fw-semibold);
}

.pagination__gap {
  display: inline-block;
  padding: 0 var(--space-xs);
  color: var(--cream-60);
}

.section--alt .pagination__gap { color: var(--undertow); }
.section--alt .pagination__status { color: var(--deep-water); }

/* The bio. Sanitized at publish, re-serialized at render, escaped by default. */
.bio { max-width: var(--max-width-text); }
.bio p { margin: 0 0 var(--space-lg); }
.bio ul,
.bio ol { margin: 0 0 var(--space-lg); padding-left: var(--space-lg); list-style: disc; }
.bio li { margin-bottom: var(--space-xs); }

/* --------------------------------------------------------------------------
   Upcoming class cards. One card per date: badge, title, when, where,
   Register. The Near Me event-card shape rebuilt from Deep Surrender parts.

   These replaced the .data-table that used to render the classes: measured at
   360px the table was 371px wide and scrolled sideways inside its container,
   the date sat mid-row as a full sentence, and the header row said "Class /
   When / Where", which is database language on a page about a person.
   -------------------------------------------------------------------------- */
.event-list {
  list-style: none;
  margin: var(--space-xl) auto 0;
  padding: 0;
  max-width: var(--max-width-text);
  display: grid;
  gap: var(--space-md);
}

.event-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-lg);
  background: var(--panel);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--cream);
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-md);
  padding: var(--space-lg) var(--space-xl);
  transition: border-color var(--ease);
}
.event-card:hover { border-color: var(--gold-40); }

/* The date, at a glance. aria-hidden in the markup: the when line under the
   title states the complete date in words, and the badge is its scannable
   twin, exactly as the monogram is to the name beside it. */
.event-card__date {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 3.75rem;
  margin: 0;
  padding: var(--space-sm) var(--space-xs);
  border: 1px solid var(--gold-40);
  border-radius: var(--radius-sm);
  background: var(--gold-10);
}

.event-card__month {
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  line-height: 1.4;
  color: var(--gold);
}

/* Playfair, like every number that matters on the site. This is what makes the
   badge read as the card's headline fact rather than as a chip. */
.event-card__day {
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  font-size: 1.6rem;
  line-height: 1.1;
  color: var(--cream);
}

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

.event-card__title {
  margin: 0 0 var(--space-xs);
  font-size: var(--fs-h4);
  color: var(--warm-sand);
}

/* The title link reads as the card's heading, exactly like .card__name a on a
   listing card: cream at rest, gold underline on hover and keyboard focus, and
   a padded line box so the tap target is not a 24px strip of text. */
.event-card__title a,
.section--alt .event-card__title a {
  color: var(--cream);
  text-decoration: none;
  display: inline-block;
  padding: 0.25rem 0;
}
.event-card__title a:hover,
.event-card__title a:focus-visible,
.section--alt .event-card__title a:hover,
.section--alt .event-card__title a:focus-visible {
  color: var(--gold);
  text-decoration: underline;
}

/* Same move as .card__name a in the base sheet's coarse block, same reason,
   same trap: the tap box grows to clear 44px without moving the text, and BOTH
   selectors are required because the .section--alt variant above carries two
   classes and a bare one-class rule here would lose to it on every profile
   whose events section lands on sand. Declared after the rules it adjusts, so
   equal specificity resolves this way on source order. Measured 32px without
   it on a 768px touch viewport. */
@media (pointer: coarse) {
  .event-card__title a,
  .section--alt .event-card__title a {
    padding-block: 0.65rem;
    margin-block: -0.65rem;
  }
}

.event-card__when {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--cream-80);
}

.event-card__where {
  margin: 0.15rem 0 0;
  font-size: var(--fs-small);
  color: var(--cream-60);
}

/* Two lines, visually only, same trade as the listing card excerpt: the full
   summary stays in the DOM for a crawler, and the listing is one click away. */
.event-card__summary {
  margin: var(--space-sm) 0 0;
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--cream-80);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* Register sits at the card's right edge, vertically centred, and never wraps
   mid-word: it is the affordance a visitor scanning for "how do I sign up" is
   looking for, so it is a real button and not a link in the prose. */
.event-card__cta {
  flex: 0 0 auto;
  align-self: center;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Contact relay. There is no published address anywhere on this site, by design.
   -------------------------------------------------------------------------- */
.relay-form {
  display: grid;
  gap: var(--space-lg);
  background: var(--panel);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--cream);
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-md);
  padding: var(--space-xl);
  max-width: var(--max-width-text);
  margin: 0 auto;
}

/* Honeypot. Off-screen rather than display:none, because some bots skip
   display:none fields. Not a text node a human can reach, and it takes no
   clicks, so it cannot interfere with selecting real copy. */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Callouts. An empty state and a form result are interruptions, so they get a
   stronger gold-40 hairline than the grid's gold-20 cards. The 3px gold side
   bar they used to carry read as a generic alert component, not as this site;
   the interruption cue is now the site's own 48px gold rule under the heading.
   -------------------------------------------------------------------------- */
.notice,
.empty {
  background: var(--panel);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--cream);
  border: 1px solid var(--gold-40);
  border-radius: var(--radius-md);
  padding: var(--space-xl);
  max-width: var(--max-width-text);
  margin-left: auto;
  margin-right: auto;
}

.notice { margin-top: var(--space-lg); margin-bottom: var(--space-lg); }

/* Centred, like every section header on the site: an empty result is the whole
   section's content, not a card that happens to be alone in the grid. */
.empty {
  margin-top: var(--space-2xl);
  padding: var(--space-2xl) var(--space-xl);
  text-align: center;
}
.empty h2 { font-size: var(--fs-h3); margin-bottom: var(--space-md); }
.empty p { max-width: 32rem; margin-left: auto; margin-right: auto; }
.empty p:last-child,
.notice p:last-child { margin-bottom: 0; }

.form-hint { font-size: var(--fs-small); color: var(--cream-60); margin: 0; }

/* Code blocks (the embed snippet). Horizontally scrollable inside their own
   container so a long URL never makes the page body scroll sideways. */
pre {
  margin: 0;
  overflow-x: auto;
  font-size: var(--fs-small);
  line-height: 1.6;
}
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

/* --------------------------------------------------------------------------
   Embeddable events widget. Its own compact shell, same tokens.
   -------------------------------------------------------------------------- */
.widget-body {
  background: var(--abyss);
  color: var(--cream);
  padding: var(--space-lg);
}

.widget-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  align-items: baseline;
  justify-content: space-between;
  border-bottom: 1px solid var(--gold-20);
  padding-bottom: var(--space-sm);
  margin-bottom: var(--space-md);
}

.widget-head h1 { font-size: var(--fs-h4); margin: 0; }

.widget-list { display: grid; gap: var(--space-md); list-style: none; margin: 0; padding: 0; }

.widget-list > li {
  background: var(--undertow-60);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius-md);
  padding: var(--space-md) var(--space-lg);
}

.widget-list h2 { font-size: 1.05rem; margin: 0 0 0.25rem; color: var(--warm-sand); }
.widget-list p { margin: 0; font-size: var(--fs-small); color: var(--cream-80); }

.widget-foot {
  margin-top: var(--space-lg);
  padding-top: var(--space-sm);
  border-top: 1px solid var(--gold-20);
  font-size: var(--fs-xs);
  color: var(--cream-60);
}

/* --------------------------------------------------------------------------
   Cross-links out to the Near Me apps and the certification training.
   -------------------------------------------------------------------------- */
.linkout {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-md);
  list-style: none;
  margin: var(--space-lg) 0 0;
  padding: 0;
}

.linkout > li {
  background: var(--panel);
  border: 1px solid var(--gold-20);
  border-radius: var(--radius-sm);
  padding: 0.5rem var(--space-lg);
}
.linkout a { color: var(--gold); }

/* --------------------------------------------------------------------------
   Responsive. The site drops section padding at 768px and again at 480px; the
   directory's own two-column filter grid lands at 640px, between them.
   -------------------------------------------------------------------------- */
@media (max-width: 768px) {
  .section { padding: var(--space-2xl) 0; }
  .hero { padding: var(--space-2xl) 0; }
  .section-header { margin-bottom: var(--space-xl); }
  .portrait--hero { width: 12rem; height: 12rem; }
  .portrait--hero .portrait__monogram,
  .portrait--hero .portrait__img::before { font-size: 3.75rem; }
  .panel-list { grid-template-columns: 1fr; }
  .btn,
  .btn-primary,
  .btn--ghost,
  .btn-ghost { max-width: 22rem; }
}

@media (max-width: 640px) {
  .relay-form,
  .card,
  .empty,
  .notice,
  .panel-list > li { padding: var(--space-lg); }

  .card-grid { grid-template-columns: 1fr; gap: var(--space-md); }

  /* Two even columns instead of five stacked rows. City sits beside State, so
     the bar is Name, City+State, button: three rows on a phone rather than a
     tower the visitor scrolls past to reach a facilitator. */
  .filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .field--q,
  .field--submit { grid-column: 1 / -1; }
  .filters .field--submit .btn { width: 100%; }

  /* The joined-bar metaphor only works while the segments are ON one bar. Once
     they stack, three borderless rows read as three labels rather than as three
     inputs, and the visitor has nothing telling them the top row is tappable.
     Below 640 each segment gets its own surface back. */
  .filters .field input {
    background: var(--undertow-60);
    border-color: var(--gold-20);
    padding-left: var(--space-md);
    padding-right: var(--space-md);
  }

  /* Two selects side by side at 390px are two illegible selects. */
  .filters__grid { grid-template-columns: 1fr; }
  .filters__grid .btn--ghost { width: 100%; }

  .results-head { gap: var(--space-xs); }
  .results-head__more { padding-top: 0; }

  /* Six city chips cost three rows and 154px at 390px, which is most of the
     reason the mobile hero ran to 965px. They are real internal links and they
     are not being hidden; they are being drawn at the size a chip row wants on
     a phone. Two rows, measured. */
  .popular { gap: 0.35rem; margin-top: var(--space-md); }
  .popular .chip { font-size: 0.72rem; letter-spacing: 0; }
  /* 0.4rem, not 0.35rem: 0.72rem text at the body's 1.8 line-height is a 20.7px
     box, and 0.35rem of padding lands the tap target at 31.9px, a tenth of a
     pixel under the 32px floor for a secondary control. Measured, not guessed. */
  .popular .chip > a { padding: 0.4rem 0.6rem; }

  .hero__trust { margin-top: var(--space-md); }

  /* 10rem, not 15rem: at 390px the wrap leaves 351px, so a 240px circle would
     be two thirds of the width and push the name and the Book button most of a
     screen down. 160px is still a third larger than the 7.5rem that shipped,
     and it leaves the h1 and the meta line above the fold. Measured at 390. */
  .profile-head { gap: var(--space-lg); }
  .portrait--hero { width: 10rem; height: 10rem; }
  .portrait--hero .portrait__monogram,
  .portrait--hero .portrait__img::before { font-size: 3.25rem; }

  /* The badge keeps the top-left corner; the button drops under the text and
     goes full width, the same treatment the hero actions get on a phone. */
  .event-card { flex-wrap: wrap; padding: var(--space-lg); }
  .event-card__body { flex: 1 1 12rem; }
  .event-card__cta { flex: 1 1 100%; align-self: stretch; text-align: center; }

  .actions .btn,
  .actions .btn--ghost { width: 100%; text-align: center; }

  /* Numbers keep their tap target; the words shrink to fit the row. */
  .pagination__step { padding: 0.7rem var(--space-sm); }
}

@media (max-width: 480px) {
  .section { padding: var(--space-xl) 0; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  .card:hover,
  .panel-list > li:hover,
  .portrait--hero:hover { transform: none; }
}

/* --------------------------------------------------------------------------
   The Certified / In Training stamp.

   Ryan, 2026-08-27: "every facilitator profile shows either a Certified stamp
   or an In Training stamp."

   This is NOT the Verified pill coming back. That one was removed on
   2026-08-12 because it repeated a claim the credential line already made in
   words, and is_verified still renders nothing anywhere. This says something
   the page cannot say without it: that two people in the same grid are at
   different stages of the same program.
   -------------------------------------------------------------------------- */

/* Shape only. Colour belongs to the two variants, so a badge that somehow
   reached the page with no variant class is visibly wrong (unstyled, inheriting
   the surface) rather than silently reading as certified. */
.badge {
  display: inline-block;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: var(--fw-semibold);
  padding: 0.3rem 0.8rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  /* The stamp shares a baseline with the name and must never be the thing that
     wraps a two-word name onto three lines. */
  white-space: nowrap;
}

/* Certified: filled gold. Both surfaces this renders on (the navy profile hero
   and the card's dark panel) are dark, so gold TEXT would be the third gold
   thing in a 200px column; the fill separates it from the headings and links
   around it. Dark text on a light fill, per the locked contrast rule. */
.badge--certified {
  background: var(--gold);
  color: var(--abyss);
  border-color: var(--gold);
}

/* In Training: quiet outline, and deliberately no gold anywhere in it. Gold is
   the site's accent; spending it here would make the two stamps read as two
   flavours of the same achievement, which is the one thing the pair exists not
   to do. Cream at 80% on a dark panel behind a 40% rule is legible and
   unemphatic: this is a status, not a demerit. */
.badge--training {
  background: transparent;
  color: var(--cream-80);
  border-color: var(--cream-40);
}

/* On a card, SIZE ONLY. A card is a 320px column already carrying a portrait, a
   name, a location, an excerpt and two chips, so the stamp is a marker on that
   stack rather than its headline. A background or color declaration appearing
   in this rule is the two surfaces starting to disagree. */
.card .badge {
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  padding: 0.15rem 0.45rem;
}
