@charset "UTF-8";
/* ============================================================== HEADER */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--navy-900);
  border-bottom: 2px solid var(--gold-600);
}
/* While a full-screen overlay (mobile nav, lightbox) has scroll locked via
   body { position: fixed }, .site-header's own `sticky` has no scrolling
   ancestor left to stick within and falls back to its plain in-flow
   position — which can land far off-screen if the page was scrolled down
   when the lock kicked in, taking the logo and the mobile nav's only close
   button with it (see lockScroll() in main.js for how this was found).
   `position: fixed` doesn't depend on a scrolling ancestor at all, so
   forcing it for the duration keeps the header correctly pinned. */
body.scroll-locked .site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: nowrap;
  gap: var(--sp-sm);
  height: var(--header-h);
}

.brand {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.brand-logo { height: 4rem; width: auto; display: block; }

/* ---- desktop nav ---- */
.site-nav { flex: 1; display: flex; justify-content: flex-end; position: relative; }
.nav-list { display: flex; gap: var(--sp-4xs); }
.nav-item { position: static; }
.nav-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  font-family: var(--font-nav);
  font-size: var(--fs-lg);
  font-weight: var(--fw-regular);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  padding: var(--sp-2xs) var(--sp-sm);
  border-radius: var(--radius);
  color: var(--white);
  transition: var(--transition);
}
.nav-item:hover .nav-link, .nav-link:focus-visible { color: var(--gold-400); }
/* current-page state — same gold the hover/focus states already use, so the
   active item simply looks permanently "hovered" rather than introducing a
   second visual language for "current" vs "hovered". */
.nav-link.is-active { color: var(--gold-400); }

/* `top: 100%` (not `calc(100% + .5rem)`) is deliberate: the panel's own
   box needs to sit flush against the trigger with zero real gap, or the
   pointer has to cross a strip that belongs to neither box on the way
   from one to the other, dropping :hover before ever reaching the panel
   — worse on some pointer/OS combinations than others, since it comes
   down to how many intermediate mousemove samples happen to land in that
   exact strip (confirmed by owner report + reproduced here by stepping a
   simulated pointer down 1px at a time and watching :hover drop out).
   This is purely a hit-testing fix, independent of `padding` below —
   moving the .5rem gap into padding-top was tried first to keep the exact
   old visual spacing, but that grows the empty space actually inside the
   panel's own border (16px -> 24px before the first item), which reads as
   too much once you're looking at the panel itself rather than measuring
   from the trigger — so padding stays plain and even on every side, and
   only `top` carries the fix.
   (Two other approaches were tried and rejected: an invisible ::before
   bridge *inside* .dropdown doesn't work because .dropdown's own
   visibility is itself conditional on .nav-item:hover already being true,
   so anything nested inside it inherits that same conditional and can't
   independently sustain the hover state that's supposed to keep it shown;
   extending .nav-item's own box via padding-bottom instead grows
   .nav-item's layout height, which grows .site-nav's height too since
   .site-nav sizes to its content — and .dropdown's `top: 100%` is
   relative to that same .site-nav, so the panel gets pushed down by a
   correlated, hard-to-predict amount instead of staying still.) */
.dropdown {
  position: absolute;
  top: 100%;
  right: 0;
  background: var(--bg-elevated);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-sm);
  min-width: 240px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: var(--transition);
  z-index: var(--z-sticky);
}
.nav-item:hover .dropdown, .nav-item:focus-within .dropdown {
  opacity: 1; visibility: visible; transform: translateY(0);
}
.nav-item.menu-force-closed .dropdown {
  opacity: 0 !important; visibility: hidden !important; pointer-events: none !important;
}
.dropdown-flat--wide { display: grid; grid-template-columns: repeat(4, minmax(190px, 1fr)); gap: var(--sp-4xs) var(--sp-sm); min-width: 760px; }
.dropdown-flat--single { display: grid; grid-template-columns: 1fr; gap: var(--sp-4xs); min-width: 200px; }
.dropdown-flat li a, .dropdown-col li a {
  display: block;
  padding: var(--sp-2xs) var(--sp-2xs);
  border-radius: var(--radius);
  font-size: var(--fs-base);
  color: var(--text-muted);
}
.dropdown-flat li a:hover, .dropdown-col li a:hover,
.dropdown-flat li a.is-active, .dropdown-col li a.is-active { background: var(--bg-sunken); color: var(--accent-strong); }

.dropdown--mega { display: grid; grid-template-columns: repeat(4, minmax(190px, 1fr)); gap: var(--sp-sm); min-width: 760px; }
.dropdown-heading {
  display: block;
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-slight);
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--sp-2xs);
  padding: var(--sp-3xs) var(--sp-2xs);
}

/* ---- header tools ---- */
.header-tools { display: flex; align-items: center; gap: var(--sp-3xs); flex-shrink: 0; color: var(--white); }
.tool-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: var(--radius);
  color: inherit;
  transition: var(--transition);
}
.tool-btn:hover { background: color-mix(in srgb, var(--white) 14%, transparent); color: var(--gold-400); }
.font-controls { display: flex; gap: var(--sp-4xs); }
.font-btn { width: auto; padding: 0 var(--sp-2xs); font-size: var(--fs-2xs); font-weight: var(--fw-bold); font-family: var(--font-sans); }
.font-btn-big { font-size: var(--fs-xs); }

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-3xs);
  width: 2.3rem;
  height: 2.3rem;
  padding: var(--sp-2xs);
}
.nav-toggle span { display: block; height: 2px; width: 100%; background: currentColor; transition: var(--transition); }
.nav-toggle.open span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle.open span:nth-child(2) { opacity: 0; }
.nav-toggle.open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ================================================================ MOBILE NAV ≤1200px */
@media (max-width: 1200px) {
  .site-nav { display: none; }
  .nav-toggle { display: flex; }
  .header-inner { gap: var(--sp-2xs); }
}
@media (max-width: 480px) {
  .brand-logo { height: 2.5rem; }
  .header-tools { gap: var(--sp-4xs); }
  .tool-btn { width: 1.9rem; height: 1.9rem; }
  .font-btn { padding: 0 var(--sp-3xs); }
}

.mobile-nav-panel {
  display: none;
  position: fixed;
  top: var(--header-h);
  left: 0; right: 0;
  /* `bottom: 0` is relative to whatever the viewport was when it was last
     computed — on iOS Safari, scrolling can collapse/expand the address
     bar and resize the *real* visible viewport without this being
     recomputed, exposing a strip of whatever's behind the panel at the
     bottom. 100dvh always tracks the actual currently-visible height, so
     the panel stays flush with the true bottom edge regardless of browser
     chrome state; the plain vh line is a harmless fallback for browsers
     old enough not to understand dvh. */
  height: calc(100vh - var(--header-h));
  height: calc(100dvh - var(--header-h));
  z-index: var(--z-header);
  background: var(--bg);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: none;    /* iOS: prevents rubber-band showing background, same as .lightbox */
  padding-bottom: env(safe-area-inset-bottom, var(--sp-md));
  border-top: 1px solid var(--border-color);
}
.mobile-nav-panel.open { display: block; }

.mobile-nav-section-toggle {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--sp-xs) var(--pad);
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: var(--fs-sm);
  border-bottom: 1px solid var(--border-color);
  color: var(--text-soft);
}
/* While a section is open, pin its own toggle (the item's name) to the top
   of the panel's scroll container — which sits flush against the header's
   bottom edge — so scrolling a long expanded list never carries the name
   out of view above the panel; it can only ever settle right under the
   top-bar, never underneath it. */
.mobile-nav-section-toggle[aria-expanded='true'] {
  position: sticky;
  top: 0;
  z-index: var(--z-base);
  background: var(--bg);
}
/* A CSS-drawn triangle, not a Unicode glyph (same reasoning as the lightbox
   prev/next arrows) — a bare character here would depend on the active
   font actually covering that codepoint, which isn't guaranteed across
   platforms; a shape drawn from plain borders always renders the same. */
.mobile-nav-section-toggle .chevron {
  flex-shrink: 0;
  width: 0; height: 0;
  margin-left: .5em;
  border-left: .3em solid transparent;
  border-right: .3em solid transparent;
  border-top: .35em solid var(--text-faint);
  transition: transform var(--transition);
}
.mobile-nav-section-toggle[aria-expanded='true'] .chevron { transform: rotate(180deg); }
.mobile-nav-section-body { padding: var(--sp-3xs) 0 var(--sp-2xs); background: var(--bg-sunken); }
.mobile-nav-section-body[hidden] { display: none; }
.mobile-nav-group-title {
  display: block;
  padding: var(--sp-2xs) var(--pad);
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: var(--fs-2xs);
  letter-spacing: var(--ls-slight);
  text-transform: uppercase;
  color: var(--accent-strong);
}
.mobile-nav-item {
  display: block;
  padding: var(--sp-2xs) var(--pad) var(--sp-2xs) calc(var(--pad) + var(--sp-xs));
  font-size: var(--fs-xs);
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-color);
}
.mobile-nav-item:hover, .mobile-nav-item.is-active { color: var(--accent-strong); }
.mobile-nav-section-toggle.is-active { color: var(--accent-strong); }

@media (max-width: 480px) {
  .mobile-nav-section-toggle, .mobile-nav-item, .mobile-nav-group-title {
    padding-left: var(--sp-sm); padding-right: var(--sp-sm);
  }
}

/* ================================================================= SEARCH */
.search-overlay {
  position: fixed; inset: 0;
  z-index: var(--z-overlay);
  background: rgba(7, 14, 31, .55);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 8vh var(--sp-sm) var(--sp-sm);
}
.search-overlay[hidden] { display: none; }
.search-box {
  width: 100%;
  max-width: 640px;
  background: var(--bg-elevated);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.search-input-row { display: flex; align-items: center; gap: var(--sp-2xs); padding: var(--sp-sm) var(--sp-sm); border-bottom: 1px solid var(--border-color); }
.search-icon { color: var(--text-faint); flex-shrink: 0; }
.search-input { flex: 1; border: 0; background: none; font-size: var(--fs-base); color: var(--text); }
.search-close { display: flex; align-items: center; justify-content: center; color: var(--text-faint); }
.search-close:hover { color: var(--text); }
.search-results { max-height: 56vh; overflow-y: auto; }
.search-result { display: flex; gap: var(--sp-xs); padding: var(--sp-xs) var(--sp-sm); cursor: pointer; }
.search-result:hover, .search-result.focused { background: var(--bg-sunken); }
.search-result-thumb { width: 48px; height: 48px; object-fit: contain; background: var(--bg-sunken); border-radius: var(--radius); flex-shrink: 0; }
.search-result-thumb-placeholder {
  width: 48px; height: 48px; border-radius: var(--radius); flex-shrink: 0;
  background: var(--bg-sunken); display: flex; align-items: center; justify-content: center; color: var(--accent);
}
.search-result-title { font-weight: var(--fw-semibold); font-size: var(--fs-sm); color: var(--text-soft); }
.search-result-title mark { background: var(--accent-soft); color: var(--ink); border-radius: var(--radius-sm); }
.search-result-meta { font-size: var(--fs-2xs); color: var(--text-faint); margin-top: var(--sp-4xs); line-height: var(--lh-normal); }
.search-result-meta mark { background: var(--accent-soft); color: var(--ink); border-radius: var(--radius-sm); }
.search-empty { padding: var(--sp-md); text-align: center; color: var(--text-faint); }
.search-footer {
  display: flex; gap: var(--sp-sm);
  padding: var(--sp-2xs) var(--sp-sm);
  border-top: 1px solid var(--border-color);
  font-size: var(--fs-3xs);
  color: var(--text-faint);
  font-family: var(--font-sans);
}

/* =============================================================== LIGHTBOX */
.lightbox {
  position: fixed; inset: 0;
  min-height: 100dvh;
  z-index: var(--z-modal);
  background: #111113;
  display: flex;
  flex-direction: column;
  overscroll-behavior: none;
}
.lightbox[hidden] { display: none; }
/* Two rows: counter/caption/close on top, prev/next arrows directly below
   the counter and close button — keeping them entirely out of the image
   area so the image itself can run edge-to-edge below this bar. */
.lightbox-topbar {
  display: grid;
  grid-template-columns: minmax(3.5rem, auto) minmax(0, 1fr) minmax(3.5rem, auto);
  grid-template-rows: auto auto;
  align-items: center;
  gap: var(--sp-3xs) var(--sp-2xs);
  padding: var(--sp-2xs) var(--sp-sm);
  flex-shrink: 0;
}
.lightbox-counter { grid-column: 1; grid-row: 1; font-family: var(--font-sans); font-size: var(--fs-xs); color: #fff; opacity: .7; justify-self: start; }
.lightbox-close {
  grid-column: 3; grid-row: 1;
  display: flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem;
  color: #fff;
  opacity: .8;
  justify-self: end;
}
.lightbox-close:hover { opacity: 1; color: var(--accent); }
.lightbox-info { grid-column: 2; grid-row: 1 / span 2; align-self: center; text-align: center; color: #fff; min-width: 0; }
.lightbox-info[hidden] { display: none; }
/* White, not --accent, and a step up the scale. The caption is read against a
   photograph filling the rest of the screen, so what matters is not the ratio
   against the backdrop — accent already gave 8.11:1 — but the ratio against
   the bright image beside it, the same relation-to-neighbour effect recorded
   for the epigraph in STYLE.md. White reaches 18.86:1 and holds against any
   photo. The description is prose, so --fs-base is its floor; the dimming it
   used to carry is dropped for the same reason, with size alone carrying the
   hierarchy between the two. */
/* font-family is set explicitly because this is an h2, and every heading
   otherwise inherits the display face. It is a caption, not a heading in the
   reading sense — it happens to be marked up as h2 so the dialog carries one
   valid heading level below the page's h1. Sans, like every other caption. */
.lightbox-info h2 { font-family: var(--font-sans); color: var(--text-on-dark); font-size: var(--fs-lg); margin-bottom: var(--sp-4xs); }
.lightbox-info p { color: var(--text-on-dark); font-size: var(--fs-base); }

.lightbox-prev, .lightbox-next {
  grid-row: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem; height: 2.75rem;
  border: none;
  background: transparent;
}
.lightbox-prev { grid-column: 1; justify-self: start; }
.lightbox-next { grid-column: 3; justify-self: end; }
/* Solid white triangles drawn with the classic border trick, not glyphs —
   keeps them visually unrelated to the photo (no font, no box) underneath. */
.lightbox-prev::after, .lightbox-next::after {
  content: '';
  width: 0; height: 0;
  border-top: .55rem solid transparent;
  border-bottom: .55rem solid transparent;
  transition: border-color var(--transition);
}
.lightbox-prev::after { border-right: .85rem solid #fff; }
.lightbox-next::after { border-left: .85rem solid #fff; }
.lightbox-prev:hover::after { border-right-color: var(--accent); }
.lightbox-next:hover::after { border-left-color: var(--accent); }

.lightbox-body { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; display: flex; justify-content: center; padding: 0; }
.lightbox-inner {
  width: 100%; height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-img {
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
}

/* ============================================================== ARTICLE PAGE */
.breadcrumbs {
  padding-top: var(--sp-sm);
  padding-bottom: var(--sp-sm);
  margin-bottom: var(--sp-3xs);
  border-bottom: 1px solid var(--border-color);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-slight);
  color: var(--text-muted);
}
.breadcrumbs a { color: var(--accent-strong); }
.breadcrumbs a:hover { color: var(--accent); text-decoration: underline; }
.breadcrumbs span:last-child { color: var(--text-soft); }
.crumb-sep { margin: 0 var(--sp-2xs); opacity: .6; }

.article-hero { padding-top: var(--sp-md); padding-bottom: var(--sp-md); }
.article-hero h1 { margin-bottom: var(--sp-2xs); }
.article-hero h1::after {
  content: '';
  display: block;
  width: 3.5rem;
  height: 3px;
  margin-top: var(--sp-xs);
  background: var(--gold-500);
}
.content-kind {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--navy-900);
  background: var(--gold-400);
  padding: var(--sp-3xs) var(--sp-2xs);
  border-radius: var(--radius);
  margin-bottom: var(--sp-2xs);
}
.article-excerpt { font-family: var(--font-display); font-style: italic; font-weight: var(--fw-light); font-size: var(--fs-lg); color: var(--text-muted); }

/* Real article images sit above the text at their native size (no crop,
   no letterbox fill, no lightbox) — they're illustrative, not a gallery. */
.article-lead-image {
  max-width: 808px; /* 750px image + 2×29px inline padding (border-box) */
  border: 1px solid var(--border-color);
  /* Article images are landscape 750×521 (ratio 1.44:1). Equal absolute padding
     looks larger at top/bottom because 24px is a bigger fraction of 521px than
     of 750px. Setting inline slightly larger compensates: 20/521 ≈ 29/750 ≈ 3.85% */
  padding-block: var(--sp-fluid-3xs);
  padding-inline: var(--sp-fluid-2xs);
  margin: 0 auto var(--sp-fluid-xs);
}
.article-lead-image img { width: 100%; height: auto; display: block; cursor: default; }

/* Same thin frame as .article-lead-image, for an article whose lead is an
   embedded YouTube player instead of a photo (e.g. an online-video-resources
   entry with a single video) — the frame box is sized to the video's own
   16:9 aspect ratio rather than a fixed photo width. */
.article-lead-video {
  width: 100%;
  max-width: 750px;
  border: 1px solid var(--border-color);
  padding: var(--sp-fluid-3xs);
  margin: 0 auto var(--sp-fluid-xs);
}
.article-lead-video-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--ink);
}
.article-lead-video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.article-body { max-width: var(--container-narrow); }

/* Measure — prose is bounded by the column, the same edge photographs,
   galleries and tables use, so text and image line up rather than the text
   stopping short of them. --measure resolves to --container-narrow (see
   tokens.css for why a character-count cap was tried and dropped).

   The rule is kept rather than deleted so the intent stays visible: prose
   width is a decision, and it is this one. Headings are excluded for the
   same reason as before — a heading wrapping earlier than its own paragraph
   looks like a mistake. */
.article-body :is(p, ul, ol, blockquote, .article-kicker, .letter-meta, .resource-source) {
  max-width: var(--measure);
}

.article-body :is(h2, h3, h4, h5, h6) { margin-top: 1.8em; }

/* Opt-in drop cap for an article's opening paragraph — pass dropCap={true}
   to ArticleLayout for a given section/route to enable it. A .section-break
   (see theology/art) starts a fresh thematic cluster within the same article,
   so the paragraph right after one gets the same treatment as a new opening.
   The :not() guards matter: when an article opens with a classed helper
   paragraph (a .verse--epigraph like theology/holy-spirit, or an
   .article-kicker), that helper IS the first <p> — without the guards the
   initial lands on the epigraph's own first letter (a "broken initial")
   while the real opening paragraph below it gets nothing. Instead the
   helper is skipped and the sibling selectors below hand the initial to
   the first real prose paragraph that follows it. */
.article-body.has-drop-cap > p:first-of-type:not(.verse):not(.article-kicker):not(.letter-meta):not(.section-break)::first-letter,
.article-body.has-drop-cap > .verse--epigraph + p::first-letter,
.article-body.has-drop-cap > .article-kicker + p:not(.verse):not(.letter-meta)::first-letter,
.article-body.has-drop-cap .section-break + p::first-letter {
  float: left;
  font-family: var(--font-display);
  font-size: 3.2em;
  line-height: .82;
  font-weight: var(--fw-semibold);
  margin: .03em .1em 0 0;
  color: var(--accent-strong);
}
.article-body img {
  border-radius: var(--radius);
  margin-block: var(--sp-2xs);
  cursor: zoom-in;
}
.article-body .article-lead-image img { cursor: default; }
.article-body ol {
  list-style: decimal;
  margin: 0 0 1.2em;
  padding-left: 1.4em;
}
.article-body ol li { padding-left: .3em; margin-bottom: .5em; }
.article-body ol li::marker { color: var(--accent); font-weight: var(--fw-semibold); }

.article-body ul { margin: 0 0 1.2em; padding-left: 0; }
.article-body ul li {
  list-style: none;
  padding-left: 1.4em;
  position: relative;
  margin-bottom: .5em;
}
.article-body ul li::before {
  content: '';
  position: absolute;
  left: .2em;
  top: .65em;
  width: 6px; height: 6px;
  border-radius: var(--radius-full);
  background: var(--accent);
}
/* =========================================================== STICKY SUBHEADER
   Rendered by ArticleLayout/ListingLayout/GalleryLayout. Sits under the
   sticky site-header; shown for as long as the page's own .breadcrumbs
   nav is at all covered by the header (see initStickySubheader() in
   main.js), so a reader deep in a long article still sees what section/
   page they're on, with no gap between the real breadcrumb disappearing
   and this one taking over. */
.sticky-subheader {
  position: sticky;
  top: var(--header-h);
  z-index: var(--z-sticky);
  max-height: 0;
  overflow: hidden;
  background: var(--navy-800);
  border-bottom: 0 solid var(--navy-600);
  opacity: 0;
  transition: max-height .25s var(--ease), opacity .2s var(--ease), border-bottom-width .25s var(--ease);
}
.sticky-subheader.is-visible { max-height: 2.75rem; opacity: 1; border-bottom-width: 1px; }
.sticky-subheader p {
  display: flex;
  align-items: center;
  gap: .5em;
  padding-block: var(--sp-xs);
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sticky-subheader-eyebrow { color: var(--gold-400); text-decoration: none; }
a.sticky-subheader-eyebrow:hover, a.sticky-subheader-eyebrow:focus-visible { color: var(--gold-300); text-decoration: none; }
.sticky-subheader-eyebrow::after { content: '·'; margin-left: .5em; color: color-mix(in srgb, var(--white) 45%, transparent); }
.sticky-subheader-title { color: var(--white); font-weight: var(--fw-medium); overflow: hidden; text-overflow: ellipsis; }

/* =============================================================== PAGE BANNER */
.page-banner {
  width: 100%;
  height: clamp(140px, 22vw, 280px);
  background-size: cover;
  background-position: center;
  position: relative;
  display: flex;
  align-items: center;
}
.page-banner .container { width: 100%; text-align: left; }
.page-banner::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, color-mix(in srgb, var(--navy-950) 78%, transparent), color-mix(in srgb, var(--navy-950) 25%, transparent));
}
.page-banner-eyebrow {
  position: relative;
  z-index: var(--z-base);
  margin: 0 0 .3em;
  color: var(--gold-400);
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-widest);
  text-transform: uppercase;
}
.page-banner-heading {
  position: relative;
  z-index: var(--z-base);
  margin: 0;
  color: var(--white);
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  letter-spacing: var(--ls-slight);
  text-transform: uppercase;
}

/* ================================================================ SHARE BAR */
.share-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-sm);
  flex-wrap: wrap;
  margin-block: var(--sp-md);
  padding-block: var(--sp-sm);
  border-top: 1px solid var(--border-color);
  border-bottom: 1px solid var(--border-color);
}
.share-bar-label {
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--accent-strong);
}
.share-bar-icons { display: flex; align-items: center; gap: var(--sp-3xs); flex-wrap: wrap; }
.share-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  border-radius: var(--radius);
  color: var(--text-muted);
  transition: var(--transition);
  flex-shrink: 0;
}
.share-btn:hover { color: var(--accent-strong); background: var(--bg-sunken); }
.share-btn svg { width: .9rem; height: .9rem; fill: currentColor; }
.share-btn-copy.copied { color: var(--accent-strong); }

/* ============================================================== ARTICLE PAGER */
.article-pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-md);
  margin-top: var(--sp-fluid-sm);
}
.article-pager-link {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
  padding: var(--sp-sm) var(--sp-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  transition: var(--transition);
}
.article-pager-link:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
.article-pager-link.is-empty { visibility: hidden; pointer-events: none; }
.article-pager-next { text-align: right; align-items: flex-end; }
.article-pager-label {
  font-family: var(--font-sans);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent-strong);
}
.article-pager-title { font-family: var(--font-display); font-size: var(--fs-sm); color: var(--text-soft); }
@media (max-width: 560px) { .article-pager { grid-template-columns: 1fr; } .article-pager-next { text-align: left; align-items: flex-start; } }

/* ------------------------------------------------------------- masonry gallery
   Columns are balanced by estimated height and step down to a single column
   at phone widths; the column-building JS lives in assets/js/main.js (see
   its masonry section). Default column count is set per gallery type by the JS
   (5 for a full gallery page — `.inline-gallery--full`, e.g.
   /person/academia/academia-photo-gallery — 4 for an inline article gallery,
   e.g. /person/bishop/election-and-ordination), computed from the gallery's
   own measured width rather than the viewport, so the same breakpoint logic
   is correct both for a full-bleed gallery page and for a gallery embedded
   in the much narrower article prose column.

   The rules below double as the no-JS/pre-hydration fallback: a plain
   responsive grid that already looks reasonable before masonryGallery.ts
   replaces the figures' flat DOM order with real `.inline-gallery-col`
   columns and flips the container to `.is-built` (flex row of columns). */
.inline-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--sp-sm);
  margin: var(--sp-md) 0;
  align-items: start;
}
.inline-gallery--full { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--sp-sm); }
.inline-gallery.is-built {
  display: flex;
  align-items: flex-start;
}
.inline-gallery-col {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: inherit;
  min-width: 0;
}
@media (max-width: 480px) {
  .inline-gallery { grid-template-columns: 1fr; }
}

.inline-gallery figure { margin: 0; cursor: zoom-in; }
/* No fixed height/object-fit — each image keeps its own natural aspect
   ratio (the defining trait of a masonry layout, vs. a uniform thumbnail
   grid), and fades in once loaded rather than popping in abruptly. */
.inline-gallery img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--bg);
  border-radius: var(--radius);
  opacity: 0;
  transition: opacity .35s ease;
}
.inline-gallery img.is-loaded { opacity: 1; }
/* Reserved space below every image for its own caption — never an
   overlay-on-hover treatment, since these captions carry real archival
   information (who/where/when), not decorative labels. */
.inline-gallery figcaption {
  font-size: var(--fs-xs);
  color: var(--text-faint);
  margin-top: var(--sp-3xs);
  line-height: var(--lh-snug);
}

/* ---------------------------------------------------------------- showcase
   The site-wide gallery card treatment (see the markup in
   election-and-ordination.md and academia-photo-gallery.md) — a calm,
   gold-on-hover bordered frame around each masonry item, plus a section
   eyebrow announcing the gallery instead of having it simply appear after
   the last paragraph. */
.gallery-eyebrow {
  margin: var(--sp-xl) 0 var(--sp-sm);
  padding-top: var(--sp-md);
  border-top: 1px solid var(--border-color);
  font-family: var(--font-sans);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--accent-strong);
}
.inline-gallery--showcase figure {
  border: 1px solid var(--border-color);
  background: var(--bg);
  padding: var(--sp-2xs);
  transition: var(--transition);
}
.inline-gallery--showcase figure:hover {
  border-color: var(--gold-500);
  box-shadow: var(--shadow-sm);
  transform: translateY(-2px);
}
/* Sans, roman — the same face as every other caption (owner's call,
   2026-08-01). Two decisions in one, both narrowing where Playfair appears:
   the italic marks another voice (quotations, epigraphs, .article-excerpt)
   and a caption is not one — it is the site speaking plainly about the
   photograph above it; and the display face is reserved for prominent
   places, which a caption is not. Only the centring distinguishes a showcase
   caption now, and that is enough, since it sits under a framed gallery card
   rather than beside body prose. */
.inline-gallery--showcase figcaption {
  text-align: center;
}

/* ------------------------------------------------------------- quote block
   The single, site-wide treatment for any quoted/highlighted passage —
   .archival-quote (a cited primary-source passage) and .pull-quote (an
   excerpt pulled from the surrounding prose with a social-share link
   instead of a <cite>) share identical visual rules: a large decorative
   gold quotation mark, a tinted panel, and the same display-italic font
   size. A bare `blockquote` with neither class (see the global.css rule)
   renders the same way too, so a missed class attribute can't produce a
   visually different quote. Do not introduce a third quote treatment —
   every "isticanje/navod" on the site must look like this one. */
.archival-quote,
.pull-quote {
  position: relative;
  margin: var(--sp-lg) 0;
  padding: var(--sp-lg) var(--sp-lg) var(--sp-md) var(--sp-2xl);
  background: var(--bg-sunken);
  border-top: 1px solid var(--border-color);
  border-bottom: 1px solid var(--border-color);
  border-left: 3px solid var(--gold-500);
}
.archival-quote::before,
.pull-quote::before {
  content: '\201C';
  position: absolute;
  top: .4rem;
  left: .85rem;
  font-family: var(--font-display);
  font-size: 3.2rem;
  line-height: var(--lh-none);
  color: var(--gold-500);
  opacity: .6;
}
.archival-quote p,
.pull-quote p {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-regular);
  font-size: var(--fs-lg);
  line-height: var(--lh-relaxed);
  color: var(--text-soft);
  margin: 0 0 var(--sp-sm);
}
.archival-quote p:last-of-type,
.pull-quote p:last-of-type { margin-bottom: 0; }
.archival-quote cite {
  display: block;
  margin-top: var(--sp-sm);
  font-family: var(--font-sans);
  font-style: normal;
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--accent-strong);
}
.archival-quote cite::before { content: '— '; color: var(--accent); }

/* source pages sometimes mark a scene/topic break within an article with a bare
   "***" — that's a typographic dinkus in the original, not a rule, so render it as
   a small centered glyph (theology/art) rather than letting markdown turn it into
   an <hr> line. */
.section-break {
  margin: var(--sp-lg) 0;
  text-align: center;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-ultra);
  color: var(--accent);
}

/* Every subheading inside an article, at any level, gets one size.

   This content mixes heading levels for a single role, because each piece
   kept whatever level its original publication happened to use. The markup
   is being normalised to ## so the document outline is honest (screen
   readers and search engines read heading levels, and h1 -> h6 claims four
   levels of nesting that do not exist). This rule is the safety net beneath
   that: whatever level survives anywhere, a section heading inside an
   article looks the same as every other one on the site.

   --fs-xl is 22 -> 26px: clearly above the 17px body text it introduces,
   clearly below the 30 -> 36px page title above it. It previously bottomed
   out at 16.8px, under body text, so a subheading rendered smaller than its
   own paragraph on a narrow window. */
.article-body :is(h2, h3, h4, h5, h6) {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-snug);
}

/* ----------------------------------------------------------- lettered list
   Source pages often enumerate items as bare "a) ... b) ..." text — restyle
   as a real <ol> (see e.g. glasgow.md, metropolitan-of-pergamon.md) with a
   hanging gold letter, instead of the generic round-bullet <ul> treatment. */
.article-body .lettered-list {
  list-style: none;
  counter-reset: lettered;
  margin: var(--sp-md) 0 var(--sp-md);
  padding-left: 0;
}
.article-body .lettered-list li {
  counter-increment: lettered;
  position: relative;
  padding-left: 2.1em;
  margin-bottom: .7em;
}
.article-body .lettered-list li::before {
  content: counter(lettered, lower-alpha) ')';
  position: absolute;
  left: 0;
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  color: var(--accent-strong);
}
/* multi-paragraph items (e.g. theology/church, theology/philosophy-and-ontology) —
   zero the default <p> margin so paragraphs inside one <li> sit tight, not doubled
   up with the li's own margin-bottom */
.article-body .lettered-list li p {
  margin: 0 0 .8em;
}
.article-body .lettered-list li p:last-child {
  margin-bottom: 0;
}
/* nested sub-enumeration (source "(i) ... (ii) ..." points within a lettered item,
   e.g. theology/church, theology/philosophy-and-ontology) — same list, roman numerals
   in parens instead of a hanging "a)" letter */
.article-body .lettered-list--roman {
  margin: var(--sp-sm) 0 0;
}
.article-body .lettered-list--roman li::before {
  content: '(' counter(lettered, lower-roman) ')';
}

/* a Greek-language parallel text enumerating with α) β) γ) instead of a) b) c)
   (e.g. library/lectures/the-church-and-the-world) — same list, native CSS
   lower-greek counter style instead of lower-alpha */
.article-body .lettered-list--greek li::before {
  content: counter(lettered, lower-greek) ')';
}

/* a source enumeration that was already Arabic-numbered ("1. 2. 3.") rather
   than lettered, but still spans multiple paragraphs per item (e.g.
   library/articles/the-mystery-of-the-church-in-orthodox-tradition) — same
   list, decimal counter with a period instead of a hanging "a)" letter */
.article-body .lettered-list--decimal li::before {
  content: counter(lettered, decimal) '.';
}

/* ------------------------------------------------------------- letter meta
   For any short block of distinct, single-line facts that belongs together
   but isn't prose — a typed letterhead/recipient address (library/letters/*),
   a credits/cast list (library/videotheque/when-god-dies), a venue/date/
   participant block. The source has each line on its own line, but plain
   Markdown joins consecutive lines of one paragraph into a run-on sentence,
   so the block needs an explicit <br>-separated <p class="letter-meta">
   instead, set apart from the prose paragraphs around it. */
.article-body .letter-meta {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  line-height: var(--lh-relaxed);
  color: var(--text-soft);
  margin: 0 0 var(--sp-md);
}

/* ---------------------------------------------------------- article kicker
   Reception/Library articles typically open with 1–3 short metadata
   fragments — author byline, original publication (often a link), a date,
   a book citation, or a descriptive subtitle — which the extractor left as
   bare paragraphs that read as broken fragments dangling under the page
   banner. Group them into a single <p class="article-kicker"> with literal
   <br> separators (same one-HTML-block convention as .letter-meta): a quiet
   sans block closed by a neutral hairline that separates the metadata from
   the prose. An optional leading <span class="kicker-subtitle"> line (a
   descriptive subtitle the source set as a small heading) renders larger,
   in Playfair italic, so it reads as a subtitle rather than a byline. */
/* One uniform size for everything in the kicker — deliberately a single
   size for the whole block (author name included; bold/italic carry the
   hierarchy, not size), sitting just under the body text's 1.0625rem so
   the metadata still reads as a preamble rather than a first paragraph.
   (Earlier iterations used .88rem with only the author's name enlarged —
   too small to read comfortably under the page banner.) */
.article-body .article-kicker {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--text-muted);
  margin: 0 0 var(--sp-lg);
  padding-bottom: var(--sp-md);
  border-bottom: 1px solid var(--border-color);
}
.article-body .article-kicker strong {
  color: var(--text-soft);
  font-weight: var(--fw-semibold);
}
.article-body .article-kicker .kicker-subtitle {
  display: block;
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-snug);
  color: var(--text-soft);
  margin-bottom: var(--sp-3xs);
}

/* ------------------------------------------------------------------- verse
   Poems and verse quotations (reception/poetry/*, epigraph poems or psalm
   verses opening an article or sermon). CommonMark collapses single
   newlines into spaces, so stanzas extracted as consecutive plain lines
   silently render as run-on prose — each stanza must instead be one
   <p class="verse"> with literal <br> line breaks (same convention as
   .letter-meta). Playfair italic, matching the site's quote language.
   .verse--epigraph is the opening-quotation variant: right-aligned and
   muted, typically closed by a
   <span class="verse-attrib"> attribution line. */
.article-body .verse {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-base);
  line-height: var(--lh-relaxed);
  margin: var(--sp-md) 0;
}
.article-body .verse--epigraph {
  text-align: right;
  color: var(--text-muted);
  margin: 0 0 var(--sp-lg);
}
.article-body .verse .verse-attrib {
  display: block;
  margin-top: var(--sp-xs);
  font-family: var(--font-sans);
  font-style: normal;
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--accent-strong);
}

/* -------------------------------------------------------------- interview Q&A
   Source interview transcripts (library/interview/*) mark the interviewer's
   question as a bare bold paragraph, with the reply opening with the
   speaker's name (e.g. "ZIZIOULAS:"). That's a turn-taking marker, not
   emphasis, so it gets a dedicated treatment — Playfair italic, a gold rule
   on the left — to read as a question, distinct from the surrounding answer
   prose and from ordinary in-body bold. */
.article-body .interview-q {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  color: var(--ink);
  border-left: 3px solid var(--accent);
  padding-left: var(--sp-sm);
  margin: var(--sp-lg) 0 var(--sp-sm);
}
.article-body .interview-q p {
  margin: 0;
}

/* ================================================================ FOOTNOTES */
.footnote-ref {
  font-size: .72em;
  vertical-align: super;
  font-weight: var(--fw-bold);
  color: var(--accent-strong);
  margin-left: .1em;
}
.footnote-back { color: var(--accent-strong); font-weight: var(--fw-bold); margin-right: .3em; text-decoration: none; }

/* ================================================================== BOARD LIST */
.board-tagline {
  margin: -.5em 0 1.3em;
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-lg);
  color: var(--text-soft);
}
.board-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  /* No row gap: each item already ends in a rule, so a gap only shows up as
     extra space ABOVE the next name that has no counterpart below the role —
     one half of a 3.4px asymmetry that was visible in the finished page. */
  gap: 0 var(--sp-lg);
  margin: 0 0 var(--sp-lg);
  padding: 0;
  list-style: none;
}
.board-list li {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .4em;
  /* Optically centred, not numerically. Equal padding leaves the text sitting
     low between the rules: the line box reserves ascender room the capitals
     do not reach, while the descenders in a name like "Yangazoglou" do use
     the space below. Measured by glyph ink, equal 8px padding gave 14.4px
     above against 12.8px below with a role, and 14.4 against 9.8 without one.
     The two cases need different corrections because what sits at the bottom
     differs — a role line in one, the name's own descenders in the other.

     The correction only ever ADDS to the bottom; the top keeps its full step
     (owner's rule, and the right one — opening a gap is safer than closing
     one, since squeezing makes a row feel cramped while a little extra air
     never does).

     One value for both cases, arrived at by measuring all 39 items rather
     than one. Ink extent varies with the string — "Protodeacon" has no
     descender, "Presbyter" does — so a single sample is not representative:
     items with a role averaged 1.3px top-heavy and reached 2.6px at worst.
     A bottom generous enough for the worst case leaves every row bottom-
     heavy by 1.8-2.7px, which is the direction to err and needs no
     :has() split. */
  padding-block: var(--sp-2xs) calc(var(--sp-xs) + var(--sp-4xs));
  padding-inline: 0;
  border-bottom: 1px solid var(--border-color);
}
/* The dot is anchored to .board-name (always one line), not the <li> —
   when .board-role wraps to a second line the <li> grows taller, and a
   dot centered on the whole <li> would drift away from the name. */
.article-body .board-list li::before { content: none; }
/* Name and role share one line-height so their half-leading matches.
   line-height adds half its slack ABOVE the glyphs and half below; at
   different sizes with the same ratio the two halves differ, and the
   difference lands entirely on one side of the row. Here it was 6.3px above
   the name against 4.9px below the role — the other half of the asymmetry.
   Equal leading brings the two within 0.7px, below noticing. */
.board-name {
  position: relative;
  font-family: var(--font-sans);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--text-soft);
}
.board-name::before {
  content: '';
  position: absolute;
  left: -1.2em;
  top: 50%;
  transform: translateY(-50%);
  width: 6px; height: 6px;
  border-radius: var(--radius-full);
  background: var(--accent);
}
.board-role {
  flex-basis: 100%;
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--text-muted);
}
.board-role::before { content: '— '; color: var(--accent); }

/* =============================================================== RESOURCE LINKS */
.resource-source {
  margin: -.3em 0 1.3em;
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--fs-base);
  color: var(--text-muted);
}
.resource-source a { color: var(--accent-strong); }
/* Hand-rolled ::before dots (ours and the inherited generic
   .article-body ul li::before) are positioned by guessing a pixel/em
   offset from the <li>'s box — that guess depends on font metrics that
   render slightly differently across browser engines, so it never
   lines up reliably for everyone. A native ::marker is the browser's
   own built-in mechanism for aligning a list bullet with an item's
   first line, with no positioning math at all — re-enable it (the
   generic .article-body ul li rule sets list-style: none and its own
   ::before, both need the extra .article-body prefix here to win the
   specificity tie) instead of faking one. */
/* ================================================================ PULL QUOTE
   Visual rules (panel, gold quote mark, font size) are shared with
   .archival-quote above; this block only adds the share-link element that
   .archival-quote doesn't have. */
.pull-quote-share {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2xs);
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  color: var(--accent-strong);
}
.pull-quote-share:hover { color: var(--accent); }

/* ============================================================ YOUTUBE EMBED */
.yt-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin: var(--sp-sm) 0 var(--sp-md);
  background: var(--ink);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.yt-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.yt-btn { display: inline-flex; margin: var(--sp-sm) 0 var(--sp-md); }
/* In-prose links sit in the same gold (--link / --accent-strong) used by
   plain non-link emphasis elsewhere on the page (eyebrows, <cite> lines,
   list markers) — color alone can't tell a reader which gold text is
   clickable. A permanent underline gives a link its own affordance that
   doesn't depend on hovering (useless on touch anyway), but a full-strength
   underline under every inline link would compete with prose; instead it
   sits at a third of the link color until hover, then fills in to full
   color — present at rest, confirmed on interaction, never shouting.
   Footnote markers (tiny superscript/return-arrow glyphs, not prose words)
   keep their own convention and opt out below. */
.article-body a:not(.btn),
.archival-quote a:not(.btn),
.pull-quote a:not(.btn),
blockquote a:not(.btn) {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  text-underline-offset: .15em;
  text-decoration-thickness: 1px;
  transition: text-decoration-color var(--transition), color var(--transition);
}
.article-body a:hover:not(.btn),
.archival-quote a:hover:not(.btn),
.pull-quote a:hover:not(.btn),
blockquote a:hover:not(.btn) {
  text-decoration-color: currentColor;
}
.article-body a.speaker-link { font-weight: var(--fw-semibold); }
.article-body a.footnote-ref,
.article-body a.footnote-back {
  text-decoration: none;
}
/* youtubeEmbed.ts deletes a <p> left fully empty once its only link becomes
   an embed/button (see the script's own cleanup pass) — belt-and-braces hide
   for the rare case any markup still leaves one behind. */
.article-body p:empty { display: none; }

/* =============================================================== MAP EMBED */
.map-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin: var(--sp-sm) 0 var(--sp-md);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.map-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.map-embed--small { aspect-ratio: 4 / 3; margin: var(--sp-xs) 0 0; }

.contact-locations {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-fluid-xs);
  margin: var(--sp-md) 0;
}
@media (max-width: 700px) { .contact-locations { grid-template-columns: 1fr; } }
.contact-location { font-size: var(--fs-sm); }
.contact-address-block { display: flex; align-items: flex-start; gap: var(--sp-sm); margin-bottom: var(--sp-sm); }
.contact-address-icon { flex-shrink: 0; margin-top: var(--sp-3xs); color: var(--accent); }
.contact-address-eyebrow {
  margin: 0 0 .3em;
  font-family: var(--font-sans);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--accent-strong);
}
.contact-address { margin: 0; line-height: var(--lh-relaxed); color: var(--text-soft); }

.contact-channels {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm);
  margin-top: var(--sp-md);
  padding-top: var(--sp-md);
  border-top: 1px solid var(--border-color);
}
.contact-channel {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-xs) var(--sp-sm);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  color: var(--text-soft);
  transition: var(--transition);
}
.contact-channel:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
.contact-channel-icon { flex-shrink: 0; color: var(--accent); }
.contact-channel-text { display: flex; flex-direction: column; gap: var(--sp-4xs); }
.contact-channel-label {
  font-family: var(--font-sans);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
  color: var(--text-faint);
}
.contact-channel-value { font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
.contact-channel:hover .contact-channel-value { color: var(--accent-strong); }

/* The .listing-grid / .listing-card card-grid styles that used to sit here
   were removed 2026-07-31 together with renderListingPage's unreachable
   grid branch (see that function's comment in tools/lib/render.mjs) — every
   listing on this site renders as .listing-row markup, styled below. */

.listing-pager { display: flex; gap: var(--sp-2xs); flex-wrap: wrap; margin-top: var(--sp-fluid-sm); }
.pager-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.4rem;
  height: 2.4rem;
  padding-inline: var(--sp-2xs);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  transition: var(--transition);
}
.pager-btn:hover { background: var(--accent-soft); color: var(--accent-strong); }
.pager-btn.is-current { background: var(--accent); color: var(--white); }
.pager-btn.is-disabled { opacity: .4; pointer-events: none; }
.listing-empty { color: var(--text-faint); font-style: italic; }

/* ---- row variant: category-root pages (e.g. /library/interview) — articles
   line up as blocks, image on top sized to its own ratio (no letterboxing). ---- */
.listing-rows {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-fluid-xs);
}
@media (max-width: 1100px) { .listing-rows { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .listing-rows { grid-template-columns: 1fr; } }
.listing-row {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: var(--transition);
}
.listing-row:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.listing-row:hover .listing-row-title { color: var(--accent); }
.listing-row-thumb { display: flex; }
.listing-row-thumb img { width: 100%; height: auto; display: block; transition: transform .4s var(--ease); }
.listing-row:hover .listing-row-thumb img { transform: scale(1.04); }
.listing-row-body { display: flex; flex-direction: column; gap: var(--sp-2xs); min-width: 0; padding: var(--sp-sm) var(--sp-sm) var(--sp-md); }
.listing-row-title { font-family: var(--font-display); font-weight: var(--fw-medium); font-size: var(--fs-lg); color: var(--text-soft); transition: var(--transition); }
.listing-row-excerpt { font-size: var(--fs-xs); color: var(--text-muted); }
/* --accent (#ee945d) measures 2.33:1 on white — fine for a large graphic
   element, far below AA for 12.8px text like this. --accent-strong is the
   same gold family at 4.96:1, and is already this site's --link colour. */
.listing-row-link { font-family: var(--font-sans); font-size: var(--fs-2xs); font-weight: var(--fw-bold); letter-spacing: var(--ls-slight); color: var(--accent-strong); text-transform: uppercase; }

/* Uniform preview blocks (ListingLayout's clampExcerpt prop — trial, currently
   only /foundation/blog): the excerpt is clamped *visually* to 3 lines, with
   the browser adding the ellipsis at the cut point — no textual truncation,
   so previews that don't end on a full stop still read cleanly. READ MORE is
   pinned to the card's bottom edge so every card in a grid row shares one
   baseline even when titles run different lengths. */
.listing-rows--clamp .listing-row-body { flex: 1; }
.listing-rows--clamp .listing-row-excerpt {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
.listing-rows--clamp .listing-row-link { margin-top: auto; padding-top: var(--sp-2xs); }

/* ================================================================= FOOTER */
.site-footer { background: var(--footer-bg); color: var(--footer-text); margin-top: var(--sp-fluid-sm); }
.footer-inner { padding-block: var(--sp-xl) var(--sp-lg); }

.footer-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-md);
  margin-bottom: var(--sp-md);
}
.footer-logo { display: block; flex-shrink: 0; }
.footer-logo img { height: 6rem; width: auto; display: block; opacity: .95; }
.footer-menu-links { display: flex; gap: var(--sp-md); flex-wrap: wrap; justify-content: flex-end; }
.footer-menu-links a {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-slight);
  color: var(--footer-text);
  opacity: .85;
}
.footer-menu-links a:hover { color: var(--gold-400); opacity: 1; }

.footer-divider { border-top: 1px solid color-mix(in srgb, var(--white) 14%, transparent); margin-bottom: var(--sp-md); }

.footer-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-md);
}
.footer-copyright { margin: 0; font-size: var(--fs-sm); color: var(--footer-text); opacity: .75; }
.footer-social { display: flex; gap: var(--sp-sm); }
.footer-social a { color: var(--footer-text); }
.footer-social a:hover { color: var(--gold-400); }

@media (max-width: 700px) {
  .footer-top { flex-direction: column; align-items: flex-start; }
  .footer-menu-links { justify-content: flex-start; }
  .footer-bottom { flex-direction: column; text-align: center; justify-content: center; }
}

/* ── Selection-share bubble ───────────────────────────────────────────────── */
.selection-share-bubble {
  position: absolute;
  left: var(--tip-x, 0);
  top: var(--tip-y, 0);
  transform: translate(-50%, calc(-100% - 10px));
  display: flex;
  align-items: center;
  gap: var(--sp-3xs);
  padding: var(--sp-3xs) var(--sp-3xs) var(--sp-3xs) var(--sp-2xs);
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius);
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-slight);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  z-index: var(--z-overlay);
  box-shadow: var(--shadow-dark-sm);
  transition: background .15s;
}
.selection-share-bubble[hidden] { display: none; }
/* downward-pointing arrow */
.selection-share-bubble::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--ink);
  transition: border-top-color .15s;
}
.selection-share-bubble:hover,
.selection-share-bubble:focus-visible {
  background: color-mix(in srgb, var(--ink) 85%, #000);
}
.selection-share-bubble:hover::after,
.selection-share-bubble:focus-visible::after {
  border-top-color: color-mix(in srgb, var(--ink) 85%, #000);
}
/* Premium "P" toggle inside the bubble */
.ssb-prem {
  background: none;
  border: 1px solid rgba(255,255,255,.25);
  color: rgba(255,255,255,.45);
  border-radius: var(--radius-lg);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-slight);
  padding: var(--sp-4xs) var(--sp-3xs);
  line-height: var(--lh-snug);
  cursor: pointer;
  transition: border-color .12s, color .12s;
  margin-left: var(--sp-4xs);
  flex-shrink: 0;
}
.ssb-prem:hover,
.ssb-prem.is-active { border-color: var(--accent); color: var(--accent); }
/* Mobile: fixed bottom bar — avoids iOS/Android native selection toolbar */
.selection-share-bubble.is-mobile {
  position: fixed;
  left: 50%;
  bottom: 5rem;
  top: auto;
  transform: translateX(-50%);
  padding: var(--sp-2xs) var(--sp-xs) var(--sp-2xs) var(--sp-xs);
  font-size: var(--fs-xs);
  gap: var(--sp-2xs);
}
.selection-share-bubble.is-mobile::after { display: none; }

/* ── Selection-share thread panel ────────────────────────────────────────── */
.selection-thread-panel {
  position: fixed;
  bottom: 1.25rem;
  right: 1.25rem;
  width: min(380px, calc(100vw - 1.5rem));
  max-height: min(480px, calc(100vh - 2.5rem));
  background: var(--ink);
  color: #fff;
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-dark-md);
  z-index: var(--z-overlay);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: var(--font-sans);
}
.selection-thread-panel[hidden] { display: none; }

.stp-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-2xs) var(--sp-sm);
  border-bottom: 1px solid rgba(255,255,255,.1);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-slight);
  flex-shrink: 0;
}
.stp-close {
  background: none;
  border: none;
  color: rgba(255,255,255,.5);
  cursor: pointer;
  font-size: var(--fs-xs);
  line-height: var(--lh-none);
  transition: color .12s;
  /* The visible "✕" glyph alone is far under a usable touch target on
     mobile — padding grows the actual tappable box well past it, and the
     matching negative margin cancels that growth for layout purposes so
     the glyph still sits flush in the header's usual top-right corner. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-xs);
  margin: -12px -10px -12px var(--sp-3xs);
}
.stp-close:hover { color: #fff; }

.stp-list { overflow-y: auto; flex: 1; }

.stp-item {
  display: grid;
  grid-template-columns: 2.2rem 1fr auto;
  gap: var(--sp-2xs) var(--sp-2xs);
  align-items: start;
  padding: var(--sp-2xs) var(--sp-sm);
  border-bottom: 1px solid rgba(255,255,255,.06);
}
.stp-item:last-child { border-bottom: none; }

.stp-num {
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  color: rgba(255,255,255,.35);
  padding-top: .15em;
  letter-spacing: var(--ls-slight);
}

.stp-text {
  font-size: var(--fs-2xs);
  line-height: var(--lh-normal);
  color: rgba(255,255,255,.82);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Thread instruction note */
.stp-info {
  font-size: var(--fs-3xs);
  /* .4 measured 3.82:1 on --ink — below the 4.5:1 floor, and this is a full
     instruction the reader has to follow, not a label to glance at. .7 gives
     9.52:1: half the contrast of the white text beside it, so it still reads
     as secondary, but clears the threshold STYLE.md records for reading
     faint next to a brighter neighbour. */
  color: rgba(255,255,255,.7);
  line-height: var(--lh-normal);
  margin: 0;
  padding: var(--sp-3xs) var(--sp-sm) var(--sp-2xs);
  border-bottom: 1px solid rgba(255,255,255,.06);
  flex-shrink: 0;
}

.stp-btn-group { display: flex; flex-direction: column; gap: var(--sp-3xs); margin-top: var(--sp-4xs); }

.stp-open-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-4xs);
  background: var(--accent);
  color: var(--ink);
  border: none;
  border-radius: var(--radius-lg);
  padding: var(--sp-3xs) var(--sp-2xs);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-bold);
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s;
}
.stp-open-btn:hover { background: var(--gold-300, #fad2ae); }

.stp-copy-btn {
  background: none;
  border: 1px solid rgba(255,255,255,.25);
  color: rgba(255,255,255,.65);
  border-radius: var(--radius-lg);
  padding: var(--sp-4xs) var(--sp-2xs);
  font-size: var(--fs-3xs);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-sans);
  transition: border-color .12s, color .12s;
}
.stp-copy-btn:hover { border-color: rgba(255,255,255,.6); color: #fff; }

/* Premium toggle at the bottom of the panel */
.stp-footer {
  border-top: 1px solid rgba(255,255,255,.08);
  padding: var(--sp-2xs) var(--sp-sm);
  flex-shrink: 0;
}
.stp-premium-label {
  display: flex;
  align-items: center;
  gap: var(--sp-2xs);
  font-size: var(--fs-3xs);
  color: rgba(255,255,255,.55);
  cursor: pointer;
  user-select: none;
}
.stp-premium-label input[type="checkbox"] { accent-color: var(--accent); cursor: pointer; }
.stp-premium-label:hover { color: rgba(255,255,255,.85); }

/* Mobile: full-width bottom sheet for the panel */
@media (max-width: 480px) {
  .selection-thread-panel {
    bottom: 0;
    right: 0;
    left: 0;
    width: 100%;
    max-height: 65vh;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  }
}

/* ===================================================== PRINT PHOTOS DIALOG
   Confirmation prompt shown by the Share block's Print button, asking
   whether to include the page's photos in the printed output (the site's
   own print stylesheet includes photos by default — this dialog's "Text
   only" choice is the opt-out; see global.css's @media print block) before
   calling window.print(). Same dark-scrim /
   centered-card pattern as the rest of the site's overlays (lightbox,
   search), not a native browser confirm() — kept visually consistent with
   the rest of the design system rather than jarring the visitor out of it
   right before they print. */
.print-photos-dialog {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-md);
  background: rgba(12, 16, 24, 0.6);
}
.print-photos-dialog[hidden] { display: none; }
.print-photos-box {
  position: relative;
  width: 100%;
  max-width: 420px;
  background: var(--bg-elevated);
  color: var(--text);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--sp-lg) var(--sp-md) var(--sp-md);
  text-align: center;
}
.print-photos-title {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  margin: 0 0 var(--sp-2xs);
}
.print-photos-desc {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  margin: 0 0 var(--sp-md);
}
.print-photos-actions {
  display: flex;
  gap: var(--sp-xs);
  justify-content: center;
  flex-wrap: wrap;
}
/* Quiet by design — a discoverability nudge (most desktop/Android print
   dialogs already surface "Save as PDF" as an obvious destination option;
   this line mainly helps iOS Safari, where it takes a pinch-zoom gesture on
   the print preview instead), not a second decision competing with the
   Yes/No choice above it. */
.print-photos-tip {
  margin: var(--sp-sm) 0 0;
  padding-top: var(--sp-sm);
  border-top: 1px solid var(--border-color);
  font-size: var(--fs-2xs);
  line-height: var(--lh-normal);
  color: var(--text-faint);
}
.print-photos-close {
  position: absolute;
  top: .6rem;
  right: .6rem;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  color: var(--text-faint);
  transition: background var(--transition), color var(--transition);
}
.print-photos-close:hover { background: var(--bg-sunken); color: var(--text); }

/* ============================================================== CHRONOLOGY
   The /person/biography/chronology timeline — a vertical gold spine with a
   dot per entry, Playfair years right-aligned against the spine, events to
   the right. `.chrono-milestone` (birth, episcopal consecration, repose)
   fills the dot and gilds the year. Pure article-body markup, same
   convention as the other content patterns — see CLAUDE.md
   "Content markup patterns". */
.article-body .chronology {
  --chrono-yearw: 5.8rem;
  --chrono-gap: var(--sp-xl);
  list-style: none;
  margin: var(--sp-xl) 0 var(--sp-2xs);
  padding: 0;
  position: relative;
}
.article-body .chronology::before {
  content: '';
  position: absolute;
  top: 1.1rem;
  bottom: 1.1rem;
  left: calc(var(--chrono-yearw) + var(--chrono-gap) / 2);
  width: 1px;
  background: linear-gradient(
    to bottom,
    transparent,
    var(--gold-500) 2.5rem,
    var(--gold-500) calc(100% - 2.5rem),
    transparent
  );
}
.article-body .chronology li {
  position: relative;
  display: grid;
  grid-template-columns: var(--chrono-yearw) minmax(0, 1fr);
  column-gap: var(--chrono-gap);
  padding-block: var(--sp-xs);
}
.article-body .chronology li::before {
  content: '';
  position: absolute;
  top: 1.32rem;
  left: calc(var(--chrono-yearw) + var(--chrono-gap) / 2);
  transform: translateX(-50%);
  width: 9px;
  height: 9px;
  border-radius: var(--radius-full);
  background: var(--bg);
  border: 2px solid var(--gold-600);
  z-index: var(--z-base);
}
.article-body .chronology .chrono-year {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: var(--fw-medium);
  line-height: var(--lh-snug);
  color: var(--text-soft);
  text-align: right;
  letter-spacing: var(--ls-slight);
}
.article-body .chronology .chrono-event {
  align-self: center;
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--text-muted);
  padding-top: var(--sp-4xs);
}
.article-body .chronology .chrono-milestone .chrono-year { color: var(--accent-strong); font-weight: var(--fw-semibold); }
.article-body .chronology .chrono-milestone .chrono-event { color: var(--text-soft); font-weight: var(--fw-medium); }
.article-body .chronology .chrono-milestone::before {
  background: var(--gold-600);
  width: 13px;
  height: 13px;
  top: 1.22rem;
}

@media (max-width: 640px) {
  /* Stacked: spine hugs the left edge, year above its event. */
  .article-body .chronology { --chrono-indent: 1.9rem; }
  .article-body .chronology::before { left: .45rem; }
  .article-body .chronology li {
    grid-template-columns: 1fr;
    row-gap: var(--sp-4xs);
    padding-left: var(--chrono-indent);
    padding-block: var(--sp-xs);
  }
  .article-body .chronology li::before { left: .45rem; top: 1.27rem; }
  .article-body .chronology .chrono-milestone::before { top: 1.14rem; }
  .article-body .chronology .chrono-year { text-align: left; font-size: var(--fs-lg); transform: translateY(-4px); }
  .article-body .chronology .chrono-event { padding-top: 0; }
}
