/* The Abide Project (discipleship_project/v1) — bundled CSS
 * Ported from the design source at "Discipleship Project - Revisited/shared.css".
 *
 * Token strategy:
 *   - Canonical Appearance tokens (--brand-primary, --bg-base, --bg-subtle,
 *     --bg-inverse, --text-primary, --text-on-inverse, --section-bg, etc.)
 *     are emitted by base.html from tenant theme. This file declares the
 *     template-local --accent-* and --paper-* variables for legacy rules and
 *     uses color-mix() to derive --accent-deep / --accent-tint so palette
 *     picks (including Custom) automatically deepen on hover and tint on bg.
 *   - body { background: ... } is NOT set here; base.html owns that.
 *   - Per docs/templates/01-source-and-assets.md §6, section-level surfaces
 *     are painted by the [data-tone] wrappers; block partials do NOT set
 *     their own outer background.
 */

:root {
  /* Editorial easing curve (animations.md §"Suggested easing"). */
  --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* Template-local accent derivations from the canonical brand token.
   * Falls back to clay (#B5532B) if --brand-primary isn't bridged yet.
   *
   * Tint ratio was 22% / white originally; bumped to 32% so the
   * resulting salmon matches the DP-Revisited prototype's hand-tuned
   * #E9D4C3 active-row background (the previous 22% blend rendered too
   * pale next to the warm-paper surface). Same calc reasoning for
   * --accent-deep — kept at 80% / black, which already resolves
   * within a couple RGB units of the prototype's #8E3F1F. */
  --accent:      var(--brand-primary, #B5532B);
  --accent-deep: color-mix(in oklab, var(--brand-primary, #B5532B) 80%, black);
  --accent-tint: color-mix(in oklab, var(--brand-primary, #B5532B) 32%, white);

  /* Paper palette — used by inner elements (placeholders, framed photos,
   * card surfaces). Surface tone roles still drive section-level paint via
   * --section-bg from base.html. */
  --paper:        var(--bg-base, #F1EADB);
  --paper-warm:   var(--bg-subtle, #ECE3D0);
  --paper-deep:   var(--bg-elevated, #E4D9C0);
  --ink:          var(--text-primary, #1C1916);
  --ink-soft:     var(--text-secondary, #2D2924);
  --ink-mute:     var(--text-muted, #6B6358);
  --ink-faint:    #94897A;
  --rule:         var(--border-default, #D7CCB5);
  --rule-soft:    #E2D8C2;
  --dark:         var(--bg-inverse, #1F1B16);
  --dark-paper:   #2A2520;
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  color: var(--ink);
  font-family: "Geist", -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
.serif { font-family: "Newsreader", Georgia, serif; font-weight: 400; letter-spacing: -0.005em; }
.mono  { font-family: "Geist Mono", ui-monospace, monospace; }
.eyebrow {
  font-family: "Geist Mono", monospace;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  font-weight: 500;
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
img { display: block; max-width: 100%; }
::selection { background: var(--accent); color: var(--paper); }

/* ─── Photo placeholder (10 tone-mats) ──────────────────────────────────
 * Shown when no image is bound. Hero blocks should always render a real
 * image (with a /static/discipleship_project/v1/images/hero.png fallback)
 * — these tones light up everywhere else in the empty/preview state. */
.photo {
  position: relative;
  overflow: hidden;
  background:
    repeating-linear-gradient(135deg, rgba(28,25,22,0.04) 0 12px, rgba(28,25,22,0.00) 12px 24px),
    linear-gradient(180deg, var(--paper-deep), var(--paper-warm));
  color: var(--ink-mute);
}
.photo .photo-label {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; justify-content: flex-start;
  padding: 16px;
  font-family: "Geist Mono", monospace;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: lowercase;
  color: var(--ink-faint);
}
.photo.tone-warm   { background: repeating-linear-gradient(135deg, rgba(28,25,22,0.05) 0 12px, transparent 12px 24px), linear-gradient(180deg, #D7C2A0, #C7AC83); }
.photo.tone-clay   { background: repeating-linear-gradient(135deg, rgba(28,25,22,0.05) 0 12px, transparent 12px 24px), linear-gradient(180deg, #C9876E, #A55B3F); }
.photo.tone-olive  { background: repeating-linear-gradient(135deg, rgba(28,25,22,0.05) 0 12px, transparent 12px 24px), linear-gradient(180deg, #8B9268, #5F6B3A); }
.photo.tone-plum   { background: repeating-linear-gradient(135deg, rgba(28,25,22,0.06) 0 12px, transparent 12px 24px), linear-gradient(180deg, #7D5675, #4F2F4F); }
.photo.tone-navy   { background: repeating-linear-gradient(135deg, rgba(28,25,22,0.06) 0 12px, transparent 12px 24px), linear-gradient(180deg, #46587C, #2A3B5E); }
.photo.tone-mustard{ background: repeating-linear-gradient(135deg, rgba(28,25,22,0.05) 0 12px, transparent 12px 24px), linear-gradient(180deg, #D9AE5C, #B5852A); }
.photo.tone-cream  { background: repeating-linear-gradient(135deg, rgba(28,25,22,0.04) 0 12px, transparent 12px 24px), linear-gradient(180deg, var(--paper-deep), var(--paper-warm)); }
.photo.tone-ink    { background: repeating-linear-gradient(135deg, rgba(255,255,255,0.04) 0 12px, transparent 12px 24px), linear-gradient(180deg, #2D2924, #1C1916); color: #B0A696; }
.photo.tone-ink .photo-label { color: #8A7F6F; }
.photo.tone-coral  { background: repeating-linear-gradient(135deg, rgba(28,25,22,0.05) 0 12px, transparent 12px 24px), linear-gradient(180deg, #C66A6A, #8E3F3F); }

/* When a real image is bound, the <img> covers the tone-mat entirely;
 * the placeholder text hides itself. Block partials should render the
 * <img> as a child of the .photo container.
 *
 * `object-position: center 30%` is Layer 1 of the thumbnail strategy
 * (see memory item "Focal-point metadata + responsive crop pinning"):
 * most uploaded thumbnails have their subject in the upper half (faces,
 * titles, key visual elements). 30% favors that. Wide screenshots cropped
 * into portrait card slots still get clipped, but the upper portion
 * usually survives. Layer 3 (per-image focal_point) is future work. */
.photo.has-image .photo-label { display: none; }
.photo > img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 30%;
  display: block;
}

/* Community block — left photo slot. Tenants drop poster-style content
 * here (Scripture infographics, reading-plan cards, group invites)
 * whose native aspect rarely matches the default 4:5 frame.
 *
 * Strategy: the slot loses its inline `aspect-ratio: 4/5` when an
 * image is bound (see the {% if not _p0_img %} guard in
 * `community/sticky_gallery.html`) and the image renders at natural
 * width × natural-aspect-derived height. The image fills the column
 * width with no inner empty space; container height adapts to
 * whatever the image needs. The 4:5 fallback only fires for the
 * placeholder (no-image) state.
 *
 * The image's `position: absolute; inset: 0` from `.photo > img`
 * gets overridden here so it flows naturally inside the container
 * instead of being stretched to fill a fixed box.
 *
 * `background: transparent` + `object-fit: contain` stay as defence
 * — if the image is constrained for any reason and doesn't reach the
 * container edges, the empty space goes invisible against the page
 * bg rather than showing the tone-gradient. */
.dp-community-left-photo.has-image > img {
  position: static;
  inset: auto;
  width: 100%;
  height: auto;
  object-fit: contain;
  object-position: center;
}
.dp-community-left-photo.has-image {
  background: transparent;
}

/* ─── Buttons + qlink ───────────────────────────────────────────────────*/
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 14px 22px;
  border-radius: var(--btn-radius, 999px);
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.005em;
  transition: transform .2s var(--reveal-ease),
              background .25s var(--reveal-ease),
              color .25s var(--reveal-ease);
}
.btn:hover { background: var(--ink-soft); }
.btn.ghost { background: transparent; color: var(--ink); }
.btn.ghost:hover { background: var(--ink); color: var(--paper); }
.btn.accent { background: var(--accent); border-color: var(--accent); color: var(--paper); }
.btn.accent:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.btn .arrow { transition: transform .25s var(--reveal-ease); }
.btn:hover .arrow { transform: translateX(3px); }

.qlink {
  display: inline-flex; align-items: baseline; gap: 8px;
  font-size: 14px; color: var(--ink);
  border-bottom: 1px solid var(--ink);
  padding-bottom: 2px;
  transition: gap .2s var(--reveal-ease);
}
.qlink:hover { gap: 12px; }
.qlink .arrow { font-size: 13px; }

/* ─── Layout wrappers ───────────────────────────────────────────────────*/
.wrap { max-width: 1280px; margin: 0 auto; padding: 0 40px; }
.wrap-tight { max-width: 1080px; margin: 0 auto; padding: 0 40px; }
.wrap-text { max-width: 760px; margin: 0 auto; padding: 0 40px; }

/* Prevent grid track blow-out from long descendants. */
.home-hero-grid > *,
.start-grid > *,
.themes-grid > *,
.series-feature-grid > *,
.series-secondary-grid > *,
.latest-grid > *,
.resources-grid > *,
.youth-grid > *,
.youth-card-grid > *,
.next-steps-grid > *,
.community-grid > *,
.community-gallery > *,
.footer-slim-grid > *,
.footer-slim-links > *,
.footer-full-top > *,
.footer-full-directory > * { min-width: 0; }

main [style*="display: grid"] > * { min-width: 0; }

.hairline { height: 1px; background: var(--rule); }

section { padding: 84px 0; }
section.tight { padding: 56px 0; }
section.dark .eyebrow { color: #A89A82; }

/* ─── Editorial primitives ──────────────────────────────────────────────*/
.lift { transition: transform .35s var(--reveal-ease); }
.lift:hover { transform: translateY(-2px); }

.seal {
  width: 64px; height: 64px;
  border-radius: 999px;
  border: 1px solid var(--ink);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 22px;
  color: var(--ink);
  background: transparent;
}

.numeral {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-weight: 300;
  color: var(--ink-faint);
  line-height: 0.85;
}

/* The platform's bundled CSS (`static/dist/css/main.css`) applies
 * `.card { border-radius: 0.5rem; padding: 1.5rem; ... }` globally.
 * Abide's editorial design uses flat slabs (especially inside hairline
 * grids where rounded corners on adjacent cards would cut the 1px rule
 * into corner gaps). Explicitly reset the radius here; padding is
 * already overridden per-instance via the inline `style` on each card. */
.card { border-radius: 0; transition: background .25s var(--reveal-ease), border-color .25s var(--reveal-ease); }
.card:hover { background: rgba(28,25,22,0.02); }

.asterism {
  text-align: center; color: var(--ink-faint);
  font-family: "Newsreader", serif;
  letter-spacing: 0.5em; font-size: 14px; padding: 8px 0;
}
.asterism::before { content: "✻ ✻ ✻"; }

/* ─── Nav dropdowns (CSS-only hover/focus state, no JS in Phase 2b) ─────
 * .dp-nav-item is the wrapper around any nav link with children. The
 * dropdown panel opens on hover or keyboard focus-within for accessible
 * tab navigation. Touch devices fall back to a tap (the trigger <a>'s
 * href still navigates if the user tap-clicks instead of long-pressing
 * — mobile drawer is a Phase 3 concern). */
.dp-nav-item:hover .dp-nav-dropdown,
.dp-nav-item:focus-within .dp-nav-dropdown {
  opacity: 1 !important;
  visibility: visible !important;
}
.dp-nav-dropdown li a:hover {
  background: rgba(28,25,22,0.025);
}

/* ─── Mobile nav drawer ─────────────────────────────────────────────────
 * Hamburger trigger button — animates from 3 horizontal lines to an X
 * when the drawer is open. Hidden on desktop (display: none inline);
 * mobile media query flips display to inline-flex. */
.dp-nav-mobile-trigger {
  width: 42px;
  height: 42px;
  border: 1px solid var(--rule-soft);
  border-radius: 50%;
  background: transparent;
  color: var(--ink);
  padding: 0;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color .2s var(--reveal-ease);
}
.dp-nav-mobile-trigger:hover {
  border-color: var(--ink);
}
.dp-nav-mobile-trigger-bars {
  width: 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.dp-nav-mobile-trigger-bars > span {
  display: block;
  height: 1.5px;
  background: currentColor;
  transition: transform .2s var(--reveal-ease), opacity .2s var(--reveal-ease);
}
.dp-nav-mobile-trigger[aria-expanded="true"] .dp-nav-mobile-trigger-bars > span:nth-child(1) {
  transform: translateY(5.5px) rotate(45deg);
}
.dp-nav-mobile-trigger[aria-expanded="true"] .dp-nav-mobile-trigger-bars > span:nth-child(2) {
  opacity: 0;
}
.dp-nav-mobile-trigger[aria-expanded="true"] .dp-nav-mobile-trigger-bars > span:nth-child(3) {
  transform: translateY(-5.5px) rotate(-45deg);
}

/* Drawer container — fixed overlay covering the viewport. Hidden by
 * default via the `hidden` HTML attribute; runtime.js removes it. */
.dp-nav-drawer {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  justify-content: flex-end;
}
.dp-nav-drawer[hidden] {
  display: none;
}
.dp-nav-drawer-scrim {
  position: absolute;
  inset: 0;
  background: rgba(28,25,22,0.42);
  backdrop-filter: saturate(140%) blur(4px);
  opacity: 0;
  transition: opacity .25s var(--reveal-ease);
}
.dp-nav-drawer.is-open .dp-nav-drawer-scrim {
  opacity: 1;
}
.dp-nav-drawer-panel {
  position: relative;
  /* Full-width on mobile so the menu reads as a full-screen overlay (matching
   * the search overlay), not a 380px side drawer. The drawer only renders
   * ≤1023px (hamburger breakpoint), so full-width is correct everywhere it shows. */
  width: 100%;
  max-width: 100%;
  height: 100%;
  background: var(--bg-base, var(--paper));
  color: var(--ink);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform .3s var(--reveal-ease);
  overflow-y: auto;
}
.dp-nav-drawer.is-open .dp-nav-drawer-panel {
  transform: translateX(0);
}
.dp-nav-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 24px;
  border-bottom: 1px solid var(--rule-soft);
  position: sticky;
  top: 0;
  background: var(--bg-base, var(--paper));
  z-index: 1;
}
.dp-nav-drawer-close {
  width: 38px;
  height: 38px;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: transparent;
  color: var(--ink-soft);
  font-size: 16px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.dp-nav-drawer-close:hover {
  background: var(--ink);
  color: var(--bg-base, var(--paper));
  border-color: var(--ink);
}
.dp-nav-drawer-body {
  display: flex;
  flex-direction: column;
  padding: 8px 24px 32px;
}
.dp-nav-drawer-section {
  border-top: 1px solid var(--rule-soft);
  padding: 20px 0;
}
.dp-nav-drawer-section:first-child {
  border-top: none;
}
.dp-nav-drawer-link {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 18px;
  padding: 10px 0;
  color: var(--ink);
}
.dp-nav-drawer-sub {
  list-style: none;
  padding: 0;
  margin: 6px 0 0;
  padding-left: 2px;
}
.dp-nav-drawer-sublink {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 18px;
  align-items: center;
  padding: 12px 0 12px 22px;
  margin-left: 4px;
  border-left: 1px solid var(--rule-soft);
  color: var(--ink);
}
.dp-nav-drawer-sublink:hover {
  border-left-color: var(--accent);
}

/* When drawer is open, lock body scroll. JS adds .dp-nav-locked. */
html.dp-nav-locked,
html.dp-nav-locked body {
  overflow: hidden !important;
}

/* ─── Header chrome ─────────────────────────────────────────────────────*/
header.site {
  position: sticky; top: 0; z-index: 50;
  background: rgba(241,234,219,0.92);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--rule-soft);
}

:root {
  --header-h: 115px;
  --space-section:    100px;
  --space-section-sm: 72px;
  --space-grid-gap:   32px;
  --space-eyebrow-h1: 28px;
  --space-h1-deck:    32px;
}
@media (max-width: 720px) {
  :root { --header-h: 132px; }
}

/* ─── Tablet composition (768–1023) ─────────────────────────────────────*/
@media (min-width: 768px) and (max-width: 1023px) {
  :root {
    --space-section:    64px;
    --space-section-sm: 48px;
    --space-grid-gap:   24px;
    --space-eyebrow-h1: 18px;
    --space-h1-deck:    22px;
  }
  /* Generic section rhythm. Sections that declare their OWN inline padding
   * (detail partials especially) opt out via `:not([style*="padding"])`
   * — otherwise the !important here would clobber their inline values.
   * Pattern documented in [reference/gotchas.md] inline-style cascade. */
  main > section:not([style*="padding"]) {
    padding-top: var(--space-section) !important;
    padding-bottom: var(--space-section) !important;
  }
  main > section.tight:not([style*="padding"]) {
    padding-top: var(--space-section-sm) !important;
    padding-bottom: var(--space-section-sm) !important;
  }

  .start-grid,
  .themes-grid,
  .resources-grid,
  .next-steps-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }

  .series-feature-grid {
    grid-template-columns: 1fr 1fr !important;
    gap: 36px !important;
    margin-bottom: 48px !important;
  }
  .series-secondary-grid {
    gap: 36px !important;
    padding-top: 40px !important;
  }
  .series-compact-card {
    grid-template-columns: 1fr 1.2fr !important;
    gap: 18px !important;
  }

  .latest-grid,
  .youth-grid,
  .community-grid {
    gap: 40px !important;
  }
  .community-grid {
    grid-template-columns: 1fr 1.2fr !important;
  }
  .community-sticky {
    position: static !important;
  }

  .home-hero { padding-top: 40px !important; padding-bottom: 56px !important; }
  .home-hero-grid { gap: 36px !important; }
  .hero-formation .hero-media .photo {
    aspect-ratio: 4 / 5 !important;
    height: auto !important;
  }
  .hero-teaching-photo {
    height: auto !important;
    min-height: 0 !important;
    aspect-ratio: 4 / 5 !important;
  }
  .hero-next-grid {
    grid-template-columns: 1fr 1fr !important;
  }

  .home-hero h1 { font-size: clamp(44px, 6vw, 64px) !important; }
  .section-head h2,
  .section-head-split h2 { font-size: clamp(32px, 4.6vw, 44px) !important; }

  .home-hero p,
  .latest-grid p,
  .youth-grid p,
  .community-grid p { max-width: 480px !important; }

  .footer-full-directory {
    grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  }
}

/* ─── Touch-target floor ────────────────────────────────────────────────*/
@media (hover: none) and (pointer: coarse) {
  .qlink {
    padding-top: 12px; padding-bottom: 12px;
    min-height: 44px; align-items: center;
  }
  .site-actions button { min-width: 44px; min-height: 44px; }
  .footer-col-link {
    display: inline-flex; align-items: center; min-height: 36px;
  }
  .footer-slim-links a,
  .footer-legal a {
    display: inline-flex; align-items: center; min-height: 36px; padding: 4px 0;
  }
}

/* ─── Desktop heroes fit within first viewport ──────────────────────────*/
@media (min-width: 1024px) {
  .home-hero {
    min-height: calc(100svh - var(--header-h));
    display: flex;
    align-items: center;
    padding-top: 24px !important;
    padding-bottom: 32px !important;
  }
  .home-hero > .wrap { width: 100%; }
  .home-hero .home-hero-grid { align-items: center; }

  .hero-formation h1 { font-size: clamp(48px, 5.4vw, 88px) !important; }
  .hero-formation .hero-media .photo {
    aspect-ratio: auto !important;
    height: calc(100svh - var(--header-h) - 96px) !important;
    max-height: 720px;
    min-height: 440px;
  }

  .hero-teaching h1 { font-size: clamp(44px, 5vw, 76px) !important; }
  .hero-teaching .home-hero-grid { min-height: 0 !important; align-items: stretch; }
  .hero-teaching .hero-teaching-photo {
    min-height: 0 !important;
    height: calc(100svh - var(--header-h) - 96px) !important;
    max-height: 720px;
  }

  .hero-next h1 { font-size: clamp(46px, 5.4vw, 80px) !important; }
  .hero-next .hero-next-grid {
    height: calc(100svh - var(--header-h) - 120px);
    max-height: 720px;
    min-height: 440px;
  }
  .hero-next .hero-next-grid .card { min-height: 0 !important; }

  /* Youth dark hero — same first-viewport treatment as the homepage
   * heroes: the section fills 100svh below the header, content centers,
   * and the portrait media frame is capped to height (overriding its
   * `aspect-ratio: 4/5`) so the whole hero fits without scrolling. */
  .youth-hero {
    min-height: calc(100svh - var(--header-h));
    display: flex;
    align-items: center;
    padding-top: 24px !important;
    padding-bottom: 32px !important;
  }
  .youth-hero > .wrap { width: 100%; }
  .youth-hero .youth-hero-grid { align-items: center; }
  .youth-hero-frame {
    aspect-ratio: auto !important;
    height: calc(100svh - var(--header-h) - 96px) !important;
    max-height: 720px;
    min-height: 440px;
  }
}

/* ─── 1440 / mid-laptop design state ────────────────────────────────────*/
@media (min-width: 1280px) and (max-width: 1599px) {
  :root {
    --space-section:    72px;
    --space-section-sm: 56px;
    --space-grid-gap:   28px;
    --space-eyebrow-h1: 22px;
    --space-h1-deck:    26px;
  }

  main > section:not([style*="padding"]) {
    padding-top: var(--space-section) !important;
    padding-bottom: var(--space-section) !important;
  }
  main > section.tight:not([style*="padding"]) {
    padding-top: var(--space-section-sm) !important;
    padding-bottom: var(--space-section-sm) !important;
  }

  .home-hero p,
  .latest-grid p,
  .youth-grid p,
  .community-grid p { max-width: 560px !important; }

  .home-hero h1      { font-size: clamp(48px, 4.8vw, 72px) !important; }
  .hero-formation h1 { font-size: clamp(48px, 4.8vw, 72px) !important; }
  .hero-teaching h1  { font-size: clamp(44px, 4.6vw, 68px) !important; }
  .hero-next h1      { font-size: clamp(46px, 4.8vw, 70px) !important; }
  .home-hero .eyebrow,
  section .eyebrow { font-size: 11.5px; }

  .section-head h2,
  .section-head-split h2 { font-size: clamp(36px, 4.2vw, 52px) !important; }
  .section-head { margin-bottom: 36px !important; }
  .section-head p { font-size: 16px !important; }

  .start-grid             { gap: 28px !important; }
  .series-feature-grid    { gap: 44px !important; margin-bottom: 56px !important; }
  .series-secondary-grid  { gap: 44px !important; padding-top: 44px !important; }
  .latest-grid,
  .community-grid         { gap: 56px !important; }
  .youth-grid             { gap: 56px !important; }

  .home-hero-grid { gap: 48px !important; }
  .closing-cta-actions { margin-top: 36px !important; }
}

/* ─── Hero variant: full-bleed image with dark overlay ──────────────────
 * This is the default homepage hero (per onboarding decision). The image
 * escapes the .wrap gutter and a 2-stop gradient keeps text legible. */
.hero-image-overlay {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding: 0 !important;
  color: var(--paper);
  background: #1C1916;
  min-height: 560px;
}
.hero-image-overlay-media {
  position: absolute; inset: 0; z-index: 0;
}
.hero-image-overlay-media img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* `image_object_position_vars` (the partial macro) emits only the
   * `--focal-*` CSS variables on the inline `style`. `object-position`
   * lives HERE so the per-viewport `@media` overrides further down
   * win via normal cascade ordering — an inline `object-position`
   * would beat every external rule and the breakpoints would never
   * fire. When no pinning exists the variable is unset and the
   * `center 35%` fallback applies. */
  object-position: var(--focal-default, center 35%);
  display: block;
}
/* Viewport aspect-ratio bands → closest canonical ratio pin. The
 * hero is full-bleed, so its container ratio matches the viewport.
 *
 * The cascade below walks band → adjacent bands (by aspect distance)
 * → `--focal-default` → `center 35%`. The reason: if the editor
 * pinned 1:1 and 4:5 but never touched 16:9, a desktop visitor would
 * otherwise see only the auto-saliency default (often `50% 50%`),
 * making per-ratio edits feel like no-ops on viewports they didn't
 * specifically pin. Falling through other PINNED ratios in distance
 * order respects the editor's intent ("the subject is here") even on
 * an un-pinned band, and only drops to default when nothing is set. */
@media (max-aspect-ratio: 9/10) {
  /* Tall portrait (phones) — primary 4:5 */
  .hero-image-overlay-media img {
    object-position: var(--focal-4x5,
                     var(--focal-1x1,
                     var(--focal-5x4,
                     var(--focal-4x3,
                     var(--focal-16x10,
                     var(--focal-16x9,
                     var(--focal-default, center 35%)))))));
  }
}
@media (min-aspect-ratio: 9/10) and (max-aspect-ratio: 11/10) {
  /* Near-square (tablets in portrait) — primary 1:1 */
  .hero-image-overlay-media img {
    object-position: var(--focal-1x1,
                     var(--focal-4x5,
                     var(--focal-5x4,
                     var(--focal-4x3,
                     var(--focal-16x10,
                     var(--focal-16x9,
                     var(--focal-default, center 35%)))))));
  }
}
@media (min-aspect-ratio: 11/10) and (max-aspect-ratio: 27/20) {
  /* Tablet landscape / narrow laptop — primary 5:4 / 4:3 */
  .hero-image-overlay-media img {
    object-position: var(--focal-5x4,
                     var(--focal-4x3,
                     var(--focal-1x1,
                     var(--focal-16x10,
                     var(--focal-4x5,
                     var(--focal-16x9,
                     var(--focal-default, center 35%)))))));
  }
}
@media (min-aspect-ratio: 27/20) and (max-aspect-ratio: 16/10) {
  /* Standard laptop / monitor — primary 16:10 */
  .hero-image-overlay-media img {
    object-position: var(--focal-16x10,
                     var(--focal-4x3,
                     var(--focal-5x4,
                     var(--focal-16x9,
                     var(--focal-1x1,
                     var(--focal-4x5,
                     var(--focal-default, center 35%)))))));
  }
}
@media (min-aspect-ratio: 16/10) {
  /* Widescreen — primary 16:9 */
  .hero-image-overlay-media img {
    object-position: var(--focal-16x9,
                     var(--focal-16x10,
                     var(--focal-4x3,
                     var(--focal-5x4,
                     var(--focal-1x1,
                     var(--focal-4x5,
                     var(--focal-default, center 35%)))))));
  }
}
.hero-image-overlay-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg,
      rgba(28,25,22,0.30) 0%,
      rgba(28,25,22,0.55) 45%,
      rgba(28,25,22,0.78) 100%
    ),
    linear-gradient(90deg,
      rgba(28,25,22,0.55) 0%,
      rgba(28,25,22,0.20) 60%,
      rgba(28,25,22,0.10) 100%
    );
}
.hero-image-overlay-content {
  position: relative; z-index: 1;
  padding-top: clamp(64px, 12vh, 140px);
  padding-bottom: clamp(56px, 10vh, 120px);
  max-width: 1280px;
}
.hero-image-overlay-content .eyebrow {
  color: rgba(241,234,219,0.78) !important;
}
.hero-image-overlay-content .btn.ghost:hover {
  background: rgba(241,234,219,0.12);
}

@media (min-width: 1024px) {
  .hero-image-overlay {
    min-height: calc(100svh - var(--header-h));
    display: flex; align-items: center;
  }
  .hero-image-overlay-content {
    width: 100%; padding-top: 32px; padding-bottom: 48px;
  }
}
@media (min-width: 1280px) and (max-width: 1599px) {
  .hero-image-overlay-title { font-size: clamp(48px, 5.4vw, 88px) !important; }
  .hero-image-overlay-deck  { max-width: 560px !important; font-size: 17px !important; }
}
@media (min-width: 768px) and (max-width: 1023px) {
  .hero-image-overlay { min-height: 520px; }
  .hero-image-overlay-title { font-size: clamp(48px, 7vw, 72px) !important; }
  .hero-image-overlay-content { padding-top: 64px; padding-bottom: 72px; }
}
@media (max-width: 720px) {
  .hero-image-overlay { min-height: 480px; }
  .hero-image-overlay-title { font-size: clamp(40px, 11vw, 60px) !important; }
  .hero-image-overlay-deck  { font-size: 16px !important; max-width: none !important; }
  .hero-image-overlay-content { padding-top: 48px; padding-bottom: 56px; }
  /* `object-position` is owned by the viewport-aspect-ratio bands above so
   * the editor's per-ratio focal pin wins. Don't hardcode it here. */
}

/* Below ~760px viewport-height, fall back to natural flow so content
   doesn't get crushed on a small laptop. */
@media (min-width: 1024px) and (max-height: 760px) {
  .home-hero {
    min-height: 0; align-items: stretch;
    padding-top: 40px !important; padding-bottom: 56px !important;
  }
  .hero-formation .hero-media .photo,
  .hero-teaching .hero-teaching-photo,
  .hero-next .hero-next-grid {
    height: auto !important; max-height: none;
  }
  .hero-formation .hero-media .photo { aspect-ratio: 4 / 5 !important; }
  .hero-image-overlay { min-height: 560px; }
  .hero-image-overlay-content { padding-top: 56px; padding-bottom: 64px; }
}

/* ─── Detail-page primitives (Phase 3a) ─────────────────────────────────
 * Styles for /contents/{article,video,audio}/<id> and /series/<slug>.
 * Templates compose detail pages from block partials at
 * public/blocks/discipleship_project/detail/*.html. Most of the layout
 * is inline-styled per-partial; this block is the prose + media-player
 * + transcript-sync chrome that benefits from stylesheet-level rules. */

/* Editorial prose for article body + series intro. Uses the body
 * `--sans` family with serif drop-cap-like first letter feel via type
 * scale, not actual drop caps. Narrow reading width. */
.dp-prose {
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.7;
  color: var(--ink);
}
/* `shared/accessibility.css` loads AFTER this file and sets
 * `[data-a11y-prose] { font-size: calc(1em * var(--a11y-font-scale,1)) }`
 * at equal specificity — which silently re-resolved the column to the
 * inherited 16px and discarded the editorial base above. Re-assert
 * (now 17px to match the homepage hero deck — the operator's chosen
 * reading-text reference) at higher specificity while preserving the
 * a11y font-scale slider. */
.dp-prose[data-a11y-prose] {
  font-size: calc(17px * var(--a11y-font-scale, 1));
}
.dp-prose > *:first-child { margin-top: 0; }
.dp-prose > *:last-child { margin-bottom: 0; }
.dp-prose h2,
.dp-prose h3,
.dp-prose h4 {
  font-family: var(--serif);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.012em;
  color: var(--ink);
  margin: 1.6em 0 0.45em;
}
.dp-prose h2 { font-size: 32px; }
.dp-prose h3 { font-size: 26px; }
.dp-prose h4 { font-size: 21px; }
/* Inter-block rhythm. Symmetric vertical margins (they collapse between
 * siblings) mirror the admin editor's reading spec so the published page
 * matches what authors see in `edit_content.html`. NOTE: keep these on the
 * element selectors (specificity 0,1,1) — a `> * + *` owl rule (0,1,0) loses
 * the tie to these resets and the column collapses to a wall of text. */
.dp-prose p { margin: 0.85em 0; }
.dp-prose a {
  color: inherit;
  border-bottom: 1px solid var(--ink-soft);
  padding-bottom: 1px;
}
.dp-prose a:hover { border-bottom-color: var(--accent); color: var(--accent); }
.dp-prose blockquote {
  margin: 1.1em 0;
  padding: 8px 0 8px 28px;
  border-left: 2px solid var(--accent);
  font-family: var(--serif);
  font-style: italic;
  font-size: 22px;
  line-height: 1.45;
  color: var(--ink);
}
.dp-prose ul,
.dp-prose ol { margin: 0.85em 0; padding-left: 1.5em; }
.dp-prose li + li { margin-top: 0.4em; }
.dp-prose img { max-width: 100%; height: auto; display: block; margin: 1.2em 0; }
.dp-prose figcaption {
  font-family: var(--sans);
  font-size: 12.5px;
  letter-spacing: 0.02em;
  color: var(--ink-mute);
  margin-top: 8px;
}

/* "Listen to this" TTS bar (detail/tts_bar.html). A slim card at the
 * read-or-listen decision point, sized to the reading column. Reuses the
 * round play-button vocabulary of .dp-audio-play (ink circle, accent hover).
 * Behaviour is in runtime.js initTtsPlayer(). */
/* .dp-tts vertical padding is set inline in tts_bar.html to opt out of the
 * generic `main > section` !important rhythm (see that file). */
.dp-tts-bar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 16px 10px 10px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--bg-elev, var(--paper, var(--bg-base)));
}
.dp-tts-play {
  width: 40px; height: 40px; flex-shrink: 0;
  border-radius: 999px;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper, var(--bg-base));
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
  transition: background .15s var(--reveal-ease), border-color .15s var(--reveal-ease);
}
.dp-tts-play:hover { background: var(--accent); border-color: var(--accent); }
.dp-tts-glyph { width: 15px; height: 15px; }
.dp-tts-glyph-play { transform: translateX(1px); }  /* optical centering */
.dp-tts-glyph[hidden] { display: none; }
.dp-tts-label {
  font-family: var(--sans);
  font-size: 13px; font-weight: 600; letter-spacing: 0.01em;
  color: var(--ink); white-space: nowrap; flex-shrink: 0;
}
.dp-tts-progress {
  flex: 1; min-width: 60px; height: 4px;
  border-radius: 999px; background: var(--rule-soft, var(--rule));
  cursor: pointer; position: relative;
}
.dp-tts-fill {
  height: 100%; width: 0%;
  border-radius: 999px; background: var(--accent);
}
.dp-tts-time {
  font-family: var(--mono);
  font-size: 11px; letter-spacing: 0.02em;
  color: var(--ink-mute); white-space: nowrap;
  font-variant-numeric: tabular-nums; flex-shrink: 0;
}
.dp-tts-speed {
  font-family: var(--mono);
  font-size: 11px; font-weight: 600;
  color: var(--ink-mute); background: transparent;
  border: 1px solid var(--rule); border-radius: 999px;
  padding: 4px 9px; cursor: pointer; flex-shrink: 0;
  transition: border-color .15s var(--reveal-ease), color .15s var(--reveal-ease);
}
.dp-tts-speed:hover { border-color: var(--accent); color: var(--accent); }
@media (max-width: 560px) {
  .dp-tts-label { display: none; }  /* keep play · progress · time · speed on phones */
}

/* Audio player + transcript chrome. Layout is inline-styled in the
 * partial; the bits below are for state — playing-state glyph swap,
 * hover, active transcript line highlight.
 *
 * The play/pause button is a single fixed-size circle that swaps
 * between two inline SVG glyphs depending on `aria-pressed`. The
 * shared transcript_audio_player.js toggles aria-pressed on play. */
.dp-audio-play {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border-radius: 999px;
  border: 1px solid var(--ink);
  background: var(--ink);
  color: var(--paper, var(--bg-base));
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  transition: background .15s var(--reveal-ease), color .15s var(--reveal-ease);
}
.dp-audio-play:hover { background: var(--accent); border-color: var(--accent); }
.dp-audio-play .dp-audio-glyph { display: none; }
.dp-audio-play[aria-pressed="false"] .dp-audio-glyph-play,
.dp-audio-play:not([aria-pressed]) .dp-audio-glyph-play { display: block; }
.dp-audio-play[aria-pressed="true"] .dp-audio-glyph-pause { display: block; }
/* Optical centering: the play triangle's visual centroid sits ~1px
 * left of its geometric center; nudge it right inside the button. */
.dp-audio-play .dp-audio-glyph-play { transform: translateX(1px); }
/* Transcript rows (KC-derived pattern, refined per the DP-Revisited
 * prototype). The resting state ships inline from the template; these
 * rules paint hover tint and the dropped border on the last row.
 *
 * The active-row salmon highlight + left-accent border + accent-deep
 * timestamp + accent-toned bare-triangle jump button live further
 * down in this file under the dedicated "DP-Revisited audio detail
 * port" section so a single source of truth controls the look. */
.dp-audio-transcript-line:last-child { border-bottom: 0 !important; }

/* Path-detail page (Phase 3d) — magazine vertical-rhythm layout.
 * Hero with 2-col grid (title block + TOC), then one section per
 * playable item with text-left / player-right split. Article items
 * appear in the TOC only and link out to /contents/article/<id>.
 * No click-to-swap stage — each player wires via the shared
 * `data-kms-*` modules auto-discovered at DOMContentLoaded. */

/* Hero + TOC */
.dp-path-toc { background: var(--bg-base, var(--paper)); }
.dp-path-toc-item {
  text-decoration: none;
  transition: color .15s var(--reveal-ease), padding-left .2s var(--reveal-ease);
}
.dp-path-toc-item:hover { color: var(--accent); padding-left: 4px; }
.dp-path-toc-item:hover .dp-path-toc-item-arrow,
.dp-path-toc-item:hover .serif {
  color: var(--accent);
}
.dp-path-toc-item-arrow {
  transition: transform .2s var(--reveal-ease), color .15s var(--reveal-ease);
}
.dp-path-toc-item:hover .dp-path-toc-item-arrow {
  transform: translateX(3px);
}

/* Per-item module rhythm. Soft top-rule between items keeps the
 * vertical stack readable without heavy separators. Generous padding
 * gives each item its own breathing room. */
.dp-path-item { /* padding + border-top set inline so the section
                    is one editorial unit even if a future variant
                    skips a border. */ }
.dp-path-item:first-of-type {
  /* The first item module follows the hero (which already has
     bottom padding) — drop the redundant top rule. */
  border-top: 0;
}
.dp-path-item-text h2 {
  /* Keep the H2 readable when it lands above an audio poster strip
     on narrower viewports — clamp already in inline style. */
}

/* Scroll-anchor offset so anchor jumps from the TOC don't bury the
 * item title under the platform header / breadcrumb chrome. */
.dp-path-item {
  scroll-margin-top: 24px;
}

/* ─── Listing pages (series/sermons/notes/teachings/paths/themes) ─────
 * Shared scaffolding lives in /blocks/discipleship_project/listing/*.
 * These rules style the bits that can't comfortably live as inline
 * style strings: segmented-pill active state, search input focus,
 * card lift, and the empty-state visibility toggle. */

/* Segmented filter — active pill flips background to ink, label to
 * paper. JS toggles `.is-active`; we don't fight inline `style="…"`
 * because the inline declares the resting state and `!important` here
 * wins the cascade for the active state. */
.dp-listing-filter-pill.is-active {
  background: var(--ink, #1F1B16) !important;
  color: var(--paper, #F1EADB) !important;
}

/* Search input under-line lift on focus. Inline style ships a
 * transparent border-bottom; we paint it on :focus-visible. */
.dp-listing-filter-search input:focus-visible {
  border-bottom-color: var(--ink, #1F1B16) !important;
}

/* Card hover lift — quiet, editorial. Same easing as reveal. */
.dp-listing-card {
  transition: transform 0.25s var(--reveal-ease, ease), box-shadow 0.25s var(--reveal-ease, ease);
}
.dp-listing-card:hover {
  transform: translateY(-3px);
}
.dp-listing-card:hover .dp-series-card-img img,
.dp-listing-card:hover .dp-sermon-card-img img,
.dp-listing-card:hover .dp-note-card-img img,
.dp-listing-card:hover .dp-path-card-img img,
.dp-listing-card:hover .dp-theme-card-img img {
  transform: scale(1.02);
}
.dp-listing-card .dp-series-card-img img,
.dp-listing-card .dp-sermon-card-img img,
.dp-listing-card .dp-note-card-img img,
.dp-listing-card .dp-path-card-img img,
.dp-listing-card .dp-theme-card-img img {
  transition: transform 0.6s var(--reveal-ease, ease);
}

/* Hidden cards (set by initListingFilter) collapse out of the grid. */
.dp-listing-card[hidden] { display: none !important; }

/* ─── Resources library (/resources) ──────────────────────────────────
 * The curated shelf + the filterable resource grid. Both grids are
 * hairline-ruled (1px gap over a --rule background). The responsive
 * rules change column COUNT only — the gap stays 1px. (This is why the
 * resource grid does NOT carry the shared `.dp-listing-grid` class:
 * that class's collapse rule widens the gap to 40px, which would break
 * the hairline. Cards still carry `.dp-listing-card` for the filter's
 * hover + `[hidden]` behaviour.) */
.dp-resource-card-img img,
.dp-resources-curated-img img {
  transition: transform 0.6s var(--reveal-ease, ease);
}
.dp-listing-card:hover .dp-resource-card-img img,
.dp-resources-curated-card:hover .dp-resources-curated-img img {
  transform: scale(1.03);
}
.dp-resources-curated-card {
  transition: background 0.25s var(--reveal-ease, ease), transform 0.25s var(--reveal-ease, ease);
}
.dp-resources-curated-card:hover {
  transform: translateY(-3px);
}
@media (max-width: 1023px) {
  .dp-page .dp-resource-grid,
  .dp-page .dp-resources-curated-grid {
    grid-template-columns: repeat(2, 1fr) !important;
  }
}
@media (max-width: 720px) {
  .dp-page .dp-resource-grid,
  .dp-page .dp-resources-curated-grid {
    grid-template-columns: 1fr !important;
  }
}

/* ─── Youth landing page (/youth) ──────────────────────────────────────
 * Reuses the homepage editorial grammar. The dark hero and closing band
 * declare tone_role:inverse (painted by the section wrapper) and carry
 * the youth lane's gold accent inline. The hairline grids (paths-grid,
 * resources, next-steps) join the hairline-restore rules above so their
 * 1px dividers survive the mobile collapse. */
.youth-path-card,
.youth-question-card,
.youth-resource-card,
.youth-step-card {
  transition: transform 0.25s var(--reveal-ease, ease), border-color 0.2s var(--reveal-ease, ease);
}
.youth-path-card:hover,
.youth-resource-card:hover,
.youth-step-card:hover {
  transform: translateY(-3px);
}
.youth-question-card:hover {
  border-color: var(--ink) !important;
}
.youth-paths-showcase > a {
  transition: border-color 0.2s var(--reveal-ease, ease);
}
.youth-paths-showcase > a:hover {
  border-color: var(--ink) !important;
}
/* Tablet — 3-col youth grids fold to 2-col. */
@media (max-width: 1100px) {
  .dp-page .youth-paths-grid,
  .dp-page .youth-resources-grid,
  .dp-page .youth-next-steps-grid,
  .dp-page .youth-questions-grid {
    grid-template-columns: repeat(2, 1fr) !important;
  }
}
/* Tablet/landscape phone — the 2-col editorial splits stack. */
@media (max-width: 860px) {
  .dp-page .youth-hero-grid,
  .dp-page .youth-featured-grid,
  .dp-page .youth-community-grid {
    grid-template-columns: 1fr !important;
    gap: 40px !important;
  }
  .dp-page .youth-community-sticky {
    position: static !important;
  }
}
/* Phone — everything is one column. */
@media (max-width: 720px) {
  .dp-page .youth-paths-grid,
  .dp-page .youth-resources-grid,
  .dp-page .youth-next-steps-grid,
  .dp-page .youth-questions-grid,
  .dp-page .youth-paths-showcase,
  .dp-page .youth-community-gallery {
    grid-template-columns: 1fr !important;
  }
}

/* ─── Search overlay (global chrome) ──────────────────────────────────
 * Injected once by base.html, hidden via the `hidden` attribute. The
 * `.dp-search-overlay[hidden]` rule below is load-bearing: a bare
 * `.dp-search-overlay { display: flex }` would otherwise override the
 * UA `[hidden]` style. `.is-open` (added a paint after `hidden` is
 * removed) drives the fade-in. */
.dp-search-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: min(4vh, 40px) 20px 20px;
  background: rgba(28, 25, 22, 0.42);
  -webkit-backdrop-filter: saturate(140%) blur(6px);
  backdrop-filter: saturate(140%) blur(6px);
  overflow-y: auto;
  opacity: 0;
  transition: opacity 0.25s var(--reveal-ease, ease);
}
.dp-search-overlay.is-open { opacity: 1; }
.dp-search-overlay[hidden] { display: none; }
.dp-search-panel {
  width: min(1100px, 100%);
  max-height: calc(100svh - min(8vh, 60px));
  overflow-y: auto;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  box-shadow: 0 32px 80px rgba(28, 25, 22, 0.28), 0 4px 14px rgba(28, 25, 22, 0.08);
  padding: clamp(20px, 2.6vw, 36px);
  transform: translateY(10px);
  transition: transform 0.3s var(--reveal-ease, ease);
}
.dp-search-overlay.is-open .dp-search-panel { transform: translateY(0); }
.dp-search-top {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  margin-bottom: 18px;
}
.dp-search-eyebrow {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.dp-search-close {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s var(--reveal-ease, ease), color 0.2s var(--reveal-ease, ease), border-color 0.2s var(--reveal-ease, ease);
}
.dp-search-close:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.dp-search-title {
  margin: 0;
  font-size: clamp(28px, 3.2vw, 44px);
  line-height: 1.02;
  font-weight: 400;
  letter-spacing: -0.018em;
}
.dp-search-form {
  margin-top: 18px;
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 14px;
  align-items: center;
  padding-bottom: 10px;
  border-bottom: 1.5px solid var(--ink);
}
.dp-search-form-icon { color: var(--ink-soft); flex-shrink: 0; }
.dp-search-input {
  border: none;
  outline: none;
  background: transparent;
  font-family: inherit;
  font-size: clamp(16px, 1.6vw, 19px);
  color: var(--ink);
  padding: 6px 0;
  min-width: 0;
}
.dp-search-submit {
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px 0 6px 8px;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.dp-search-submit:hover { color: var(--accent); }
.dp-search-teach-cta {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 20px;
  align-items: center;
  margin-top: 22px;
  padding: 18px 22px;
  background: var(--paper-warm, #ECE3D0);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  text-decoration: none;
  color: inherit;
  transition: background 0.2s var(--reveal-ease, ease);
}
.dp-search-teach-cta:hover { background: rgba(28, 25, 22, 0.04); }
.dp-search-teach-eyebrow {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}
.dp-search-teach-title {
  margin: 6px 0 4px;
  font-size: clamp(20px, 2.2vw, 26px);
  line-height: 1.1;
  font-weight: 400;
}
.dp-search-teach-deck {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.dp-search-teach-arrow { font-size: 20px; color: var(--ink-soft); }
.dp-search-map {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  margin-top: 26px;
}
.dp-search-map-col { display: flex; flex-direction: column; gap: 10px; }
.dp-search-map-label {
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  margin-bottom: 2px;
}
.dp-search-map-col a {
  font-family: var(--serif, Georgia, serif);
  font-size: 17px;
  color: var(--ink);
  text-decoration: none;
  transition: color 0.15s var(--reveal-ease, ease);
}
.dp-search-map-col a:hover { color: var(--accent); }
.dp-search-hint {
  margin-top: 22px;
  padding-top: 14px;
  border-top: 1px solid var(--rule-soft);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.dp-search-kbd {
  display: inline-block;
  padding: 2px 7px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  background: var(--paper-warm, #ECE3D0);
}
@media (max-width: 720px) {
  .dp-search-map { grid-template-columns: 1fr !important; gap: 24px; }
  .dp-search-teach-cta { grid-template-columns: 1fr !important; }
  .dp-search-teach-arrow { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .dp-search-overlay,
  .dp-search-panel { transition: none; }
}

/* ─── Next-step router (floating pill + drawer) ────────────────────────
 * Editor block that renders a fixed pill (bottom-left, clear of the
 * bottom-right chat widget) opening a left-anchored drawer of intent-led
 * route cards. `[hidden]` on the overlay is load-bearing — see the
 * search-overlay note above.
 *
 * The block's own `<section>` paints nothing (pill + overlay are both
 * fixed) — `padding: 0` cancels the generic `section { padding: 84px 0 }`
 * rule, which would otherwise leave an empty strip in the page flow.
 * This holds in the site-editor preview too: the block stays a floating
 * pill there, and selecting it opens the drawer modal. */
.dp-router { padding: 0; }
.dp-router-pill {
  position: fixed;
  left: 24px;
  bottom: 24px;
  z-index: 90;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 13px 22px;
  background: var(--ink, #1F1B16);
  color: var(--paper, #F1EADB);
  border: none;
  border-radius: 999px;
  font-family: 'Geist Mono', ui-monospace, monospace;
  font-size: 12px;
  letter-spacing: 0.08em;
  cursor: pointer;
  box-shadow: 0 8px 28px rgba(28, 25, 22, 0.26);
  transition: transform 0.2s var(--reveal-ease, ease);
}
.dp-router-pill:hover { transform: translateY(-2px); }
.dp-router-pill-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent, #B5532B);
}
.dp-router-overlay {
  position: fixed;
  inset: 0;
  z-index: 190;
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  padding: min(5vh, 40px) 20px;
  background: rgba(28, 25, 22, 0.42);
  -webkit-backdrop-filter: saturate(140%) blur(6px);
  backdrop-filter: saturate(140%) blur(6px);
  overflow-y: auto;
  opacity: 0;
  transition: opacity 0.25s var(--reveal-ease, ease);
}
.dp-router-overlay.is-open { opacity: 1; }
.dp-router-overlay[hidden] { display: none; }
.dp-router-panel {
  width: min(460px, 100%);
  max-height: calc(100svh - min(10vh, 80px));
  overflow-y: auto;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--rule);
  box-shadow: 0 32px 80px rgba(28, 25, 22, 0.28);
  padding: clamp(24px, 3vw, 36px);
  position: relative;
  transform: translateX(-18px);
  transition: transform 0.3s var(--reveal-ease, ease);
}
.dp-router-overlay.is-open .dp-router-panel { transform: translateX(0); }

.dp-router-close {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  background: transparent;
  color: var(--ink-soft);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s var(--reveal-ease, ease), color 0.2s var(--reveal-ease, ease), border-color 0.2s var(--reveal-ease, ease);
}
.dp-router-close:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.dp-router-title {
  margin: 0;
  padding-right: 44px;
  font-size: clamp(24px, 2.8vw, 34px);
  line-height: 1.08;
  font-weight: 400;
  letter-spacing: -0.015em;
}
.dp-router-subtitle {
  margin: 10px 0 0;
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.dp-router-notice {
  margin-top: 20px;
  padding: 12px 16px;
  background: var(--paper-warm, #ECE3D0);
  border: 1px solid var(--rule-soft);
  display: flex;
  gap: 12px;
  align-items: flex-start;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-soft);
}
.dp-router-cards {
  margin-top: 24px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
}
.dp-router-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px;
  background: var(--paper);
  text-decoration: none;
  color: inherit;
  transition: background 0.18s var(--reveal-ease, ease);
}
.dp-router-card:hover { background: var(--paper-warm, #ECE3D0); }
.dp-router-card-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.dp-router-card-title {
  font-size: 17px;
  line-height: 1.2;
  font-weight: 400;
}
.dp-router-card-sub {
  font-size: 13px;
  color: var(--ink-soft);
  line-height: 1.4;
}
.dp-router-card-arrow {
  font-size: 15px;
  color: var(--ink-mute);
  flex-shrink: 0;
  transition: transform 0.18s var(--reveal-ease, ease), color 0.18s var(--reveal-ease, ease);
}
.dp-router-card:hover .dp-router-card-arrow {
  transform: translateX(3px);
  color: var(--accent);
}
.dp-router-footer {
  margin-top: 18px;
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
@media (max-width: 720px) {
  /* Collapse to an icon-only circle so the pill no longer overruns the
   * hero CTAs / chat FAB on small screens. Mirrors the chat widget's
   * round launcher in the opposite corner. Accessible name preserved via
   * the button's aria-label. */
  .dp-router-pill {
    left: 16px;
    bottom: 16px;
    width: 52px;
    height: 52px;
    padding: 0;
    gap: 0;
    justify-content: center;
  }
  .dp-router-pill-label { display: none; }
  .dp-router-pill-dot { width: 9px; height: 9px; }
}
@media (prefers-reduced-motion: reduce) {
  .dp-router-pill,
  .dp-router-overlay,
  .dp-router-panel,
  .dp-router-card,
  .dp-router-card-arrow { transition: none; }
}

/* Grow-on-play for video items. Default split is 0.85fr / 1.15fr
 * (meta left, player right). After the user clicks play the section
 * picks up `.is-playing` from runtime.js → the grid shifts so the video
 * column grows ~25% (0.575 → ~0.72 of the row) and the meta column
 * gives up the width. The meta block itself scales down a notch so its
 * type + spacing recede without greying out — it stays fully legible,
 * just quieter, and the eye settles on the larger video. */
.dp-path-item-grid {
  /* Default split: meta left, player right. Declared here (not inline on
   * the partial) so `.is-playing` and the mobile 1-col rule can override
   * it — an inline value would beat both. */
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  transition: grid-template-columns 0.45s var(--reveal-ease, ease);
}
.dp-path-item.is-playing .dp-path-item-grid {
  grid-template-columns: minmax(0, 0.56fr) minmax(0, 1.44fr);
}
.dp-path-item-text {
  transition: transform 0.4s var(--reveal-ease, ease);
  transform-origin: left top;
}
.dp-path-item.is-playing .dp-path-item-text {
  /* Scale (not fade) — font + spacing recede together, no grey-out. */
  transform: scale(0.9);
}

/* Error-page hover state. The link list mirrors the editorial sidebar
 * pattern used elsewhere; the arrow eases right on hover. */
.dp-error-link { transition: padding-left .2s var(--reveal-ease); }
.dp-error-link:hover { color: var(--accent); padding-left: 6px; }
.dp-error-link:hover .dp-error-link-arrow {
  color: var(--accent);
  transform: translateX(3px);
}
.dp-error-link-arrow { transition: transform .2s var(--reveal-ease), color .15s var(--reveal-ease); }

/* Video detail viewport-fit. At desktop+ with a sane viewport height,
 * cap the player width so the full 16:9 frame fits below the hero +
 * header on a 1440×900 viewport without scroll. The math:
 *
 *   max-width = (viewport-height − reserved-chrome) × 16 / 9
 *
 * where `reserved-chrome` covers the platform header + breadcrumbs +
 * media-hero + the 16px gap above the player + a small safety margin.
 * On taller viewports the calc'd width exceeds the container's
 * 1200px wrap, so the player just fills the wrap as before. On
 * shorter viewports the player scales down proportionally, kept
 * centered with `margin: 0 auto`. Aspect-ratio stays 16:9 via the
 * inline `aspect-ratio` on `.video-player`. */
@media (min-width: 1024px) and (min-height: 700px) {
  .dp-video-player .video-player {
    /* Reserve ~340px for: platform header (116) + breadcrumbs (22 top
     * pad + 60 bottom pad + ~18 content = ~100) + media-hero (~200)
     * + a small safety margin. The 16:9 aspect-ratio on the player
     * keeps the height proportional. */
    max-width: calc((100vh - 340px) * 16 / 9);
    /* Left-aligned, not centered. Editorial pages anchor against the
     * left grid line; centering a sub-container-width player floats it
     * awkwardly inside the wrap. */
    margin-left: 0;
    margin-right: auto;
  }
  /* The meta strip that immediately follows the video player inherits
   * the same width clamp, anchored to the same left edge. Adjacent-
   * sibling selector keeps audio_detail's meta (which follows the
   * audio_hero, not the video_player) at the full wrap width. */
  .dp-video-player + .dp-media-meta .video-detail-meta-grid {
    max-width: calc((100vh - 340px) * 16 / 9);
    margin-left: 0;
    margin-right: auto;
  }
}

/* Plyr custom skin: paint the platform-supplied controls with Abide's
 * accent / paper so the editorial palette stays coherent. Plyr wraps
 * the `<video>` in `<div class="plyr">` at init — the class is on the
 * WRAPPER, not the original `<video>`. Scope to `.dp-page .plyr` so
 * every Plyr instance inside an Abide page is skinned, regardless of
 * which custom class we put on the `<video>` element. */
.dp-page .plyr {
  --plyr-color-main: var(--accent);
  --plyr-video-control-color: var(--paper, var(--bg-base));
  --plyr-video-control-color-hover: var(--paper, var(--bg-base));
  --plyr-video-control-background-hover: var(--accent);
  --plyr-range-track-height: 4px;
}

/* ─── Density modifier (Tweaks-Panel idea, kept for editor parity) ──────*/
body[data-density="minimal"] .density-meta { display: none; }
body[data-density="editorial"] .density-extra { display: block; }
body:not([data-density="editorial"]) .density-extra { display: none; }

/* ─── Animation primitives ──────────────────────────────────────────────
 * reveal.js attribute-driven scroll-reveal lives in reveal.js; the CSS
 * keyframes here cover the always-on micro-animations from the source
 * (dropdown/search fade-in, featured-teaching status dot pulse).
 *
 * Per animations.md, the bespoke entrance effects (hero image-settle
 * 1.03→1.00, mask wipes, hover image zoom) are wired in runtime.js so
 * they can read template-local class names. */
.fade-in { animation: dp-fade .5s var(--reveal-ease) both; }
@keyframes dp-fade {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@keyframes dp-pulse {
  0%, 100% { opacity: 0.4; transform: scale(0.9); }
  50%      { opacity: 1;   transform: scale(1.15); }
}

/* Image-settle entrance for hero <img> elements. runtime.js stamps the
 * .is-settling class on mount; the keyframe runs once. */
.dp-img-settle {
  animation: dp-img-settle 1.2s var(--reveal-ease) both;
}
@keyframes dp-img-settle {
  from { transform: scale(1.03); opacity: 0.001; }
  to   { transform: scale(1.00); opacity: 1; }
}

/* Hover micro-interactions per animations.md (1.02–1.03 image zoom on
 * cards). Restrained — never beyond 1.03. */
.lift .photo > img,
.card .photo > img {
  transition: transform .35s var(--reveal-ease);
}
.lift:hover .photo > img,
.card:hover .photo > img {
  transform: scale(1.025);
}

/* prefers-reduced-motion respect: strip translations + scale, keep opacity.
 * reveal.js handles its own attribute-driven elements; this catches our
 * always-on CSS animations. */
@media (prefers-reduced-motion: reduce) {
  .fade-in,
  .dp-img-settle {
    animation: none !important;
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
  .btn, .qlink, .lift, .card,
  .lift .photo > img, .card .photo > img {
    transition: none !important;
  }
  .lift:hover, .card:hover,
  .lift:hover .photo > img, .card:hover .photo > img {
    transform: none !important;
  }
}

/* ─── Responsive homepage frame (≤1100, ≤860, ≤720) ─────────────────────*/
@media (max-width: 1100px) {
  .wrap,
  .wrap-tight,
  .wrap-text {
    padding-left: 32px;
    padding-right: 32px;
  }
  .site-mainbar { gap: 20px !important; }
  .site-nav { justify-content: flex-end !important; }

  .latest-grid,
  .youth-grid,
  .community-grid,
  .footer-slim-grid,
  .footer-full-top {
    gap: 48px !important;
  }

  .start-grid,
  .themes-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }

  .resources-grid,
  .next-steps-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }

  .footer-full-directory {
    grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  }
}

@media (max-width: 860px) {
  section { padding-top: 68px; padding-bottom: 68px; }

  .home-hero,
  .hero-teaching,
  .hero-next,
  .hero-formation {
    padding-top: 42px !important; padding-bottom: 72px !important;
  }

  .latest-grid,
  .youth-grid,
  .community-grid,
  .footer-slim-grid,
  .footer-full-top {
    grid-template-columns: 1fr !important;
  }

  .hero-next-grid,
  .youth-card-grid,
  .community-gallery,
  .footer-slim-links {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }

  .series-feature-grid {
    grid-template-columns: 1fr !important;
    gap: 36px !important;
    margin-bottom: 52px !important;
  }
  .series-secondary-grid {
    grid-template-columns: 1fr !important;
    gap: 36px !important;
    padding-top: 40px !important;
  }

  .community-sticky { position: static !important; }

  .footer-newsletter-wrap { justify-content: flex-start !important; }

  .footer-colophon {
    align-items: flex-start !important;
    flex-direction: column !important;
    gap: 14px !important;
  }
}

/* ─── Mobile/tablet header transition (≤1023px) ─────────────────────────
 * Verbatim port of source shared.css:1670–1700. Switches the header from
 * desktop layout (3-col: wordmark | nav | actions) to mobile layout
 * (2-col: wordmark | actions, with actions carrying search + hamburger).
 * Source put this at the 1023px breakpoint so tablets get the hamburger
 * too — phones AND iPad-portrait. Keep it there. */
@media (max-width: 1023px) {
  .site-mainbar {
    grid-template-columns: 1fr auto !important;
    gap: 18px !important;
  }
  /* Hide the desktop horizontal nav links + the primary CTA (which
   * carries class `btn`). Search icon, language toggle, and hamburger
   * remain visible. */
  .site-nav,
  .site-actions .btn {
    display: none !important;
  }
  .site-actions {
    gap: 8px !important;
  }
  /* Source styles BOTH <button> and <a> action items at 42×42 on mobile,
   * so the search anchor + hamburger button line up at the same hit
   * size. */
  .site-actions a[aria-label],
  .site-actions button {
    width: 42px !important;
    height: 42px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }
  .dp-nav-mobile-trigger {
    display: inline-flex !important;
  }
  /* Listing grids drop from 3-col to 2-col at tablet width. The
   * filter-bar hero grid keeps its desktop split until phone. */
  .dp-page .dp-listing-grid {
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 40px !important;
  }
}

/* ─── Mobile (≤720px): the home-page composition collapses to 1-col ─────
   This block does the heavy lifting of mobile responsiveness. The trick:
   block partials use INLINE `grid-template-columns: …` styles for
   composition; inline styles normally beat stylesheet rules, but
   attribute-selector matches (`[style*="…"]`) + `!important` win the
   cascade. The attribute selectors below catch every common grid pattern
   our inline partials emit and force them to 1fr on mobile. Same trick
   the source design used; without it, inline-styled grids stay multi-col
   on phones. */
@media (max-width: 720px) {
  /* Wrap padding shrinks for thumb reach. */
  .wrap,
  .wrap-tight,
  .wrap-text {
    padding-left: 24px;
    padding-right: 24px;
  }

  /* Utility strip wraps to two lines instead of clipping. */
  .site-utility-inner {
    height: auto !important;
    min-height: 32px !important;
    padding-top: 8px !important;
    padding-bottom: 8px !important;
    justify-content: center !important;
    text-align: center !important;
    gap: 8px 14px !important;
    flex-wrap: wrap !important;
    line-height: 1.35 !important;
  }
  /* Header mobile refinement: shorter mainbar height + tighter gap.
   * The 2-col grid switch + hide-desktop-nav happens at the ≤1023px
   * breakpoint above (verbatim port of source line 1670). */
  .site-mainbar {
    height: auto !important;
    min-height: 76px !important;
    gap: 16px !important;
  }

  /* Catch-all: every inline-styled multi-col grid in the page body
     collapses to 1fr on mobile. Scoped to `.dp-page` (the inner
     `<main class="dp-page">` from variants/discipleship_project/v1/base.html),
     NOT `main` — because the platform's parent base.html already wraps the
     whole rendered region in `<main class="flex-1">`, which would otherwise
     pull header chrome (`.site-mainbar`'s `grid-template-columns: auto 1fr auto`
     inline style) into this collapse and break the mobile header layout. */
  .dp-page [style*="grid-template-columns: 1.05fr"],
  .dp-page [style*="grid-template-columns: 0.9fr"],
  .dp-page [style*="grid-template-columns: 1.15fr"],
  .dp-page [style*="grid-template-columns: 1.85fr"],
  .dp-page [style*="grid-template-columns: 1.4fr"],
  .dp-page [style*="grid-template-columns: 1fr 1fr"],
  .dp-page [style*="grid-template-columns: 1fr 1.1fr"],
  .dp-page [style*="grid-template-columns: 1fr 1.2fr"],
  .dp-page [style*="grid-template-columns: 0.85fr"],
  .dp-page [style*="grid-template-columns: 1.05fr 0.95fr"],
  .dp-page [style*="grid-template-columns: 1.4fr 1fr"],
  .dp-page [style*="grid-template-columns: 1fr auto"],
  .dp-page [style*="grid-template-columns: auto 1fr"],
  .dp-page [style*="grid-template-columns: repeat(2"],
  .dp-page [style*="grid-template-columns: repeat(3"],
  .dp-page [style*="grid-template-columns: repeat(4"],
  .dp-page [style*="grid-template-columns: minmax"] {
    grid-template-columns: 1fr !important;
  }
  /* Generous gap on collapsed grids — unless they're hairline grids,
     handled below. */
  .dp-page [style*="display: grid"][style*="grid-template-columns"] {
    gap: min(32px, 8vw) !important;
  }

  /* Hairline-grid sections (cards on a rule-colored background, joined
     by 1px gaps as dividers). Restore the 1px gap so the visual rhythm
     doesn't break into wide tan bands between stacked cards.
     Scoped to `.dp-page` to match the collapse rule above — otherwise the
     broad `[style*="display: grid"][style*="grid-template-columns"]` rule
     wins on specificity and stamps `gap: min(32px, 8vw)` over the 1px. */
  .dp-page .themes-grid[style],
  .dp-page .resources-grid[style],
  .dp-page .next-steps-grid[style],
  .dp-page .hero-next-grid[style],
  .dp-page .dp-resource-grid[style],
  .dp-page .dp-resources-curated-grid[style],
  .dp-page .youth-paths-grid[style],
  .dp-page .youth-resources-grid[style],
  .dp-page .youth-next-steps-grid[style],
  .dp-page .series-cinematic-grid[style] {
    gap: 1px !important;
  }
  /* Hairline-grid card sizing — desktop min-height leaves a tall hollow
     on mobile; collapse it so each card hugs its content. */
  .dp-page .themes-grid[style] > [class*="card"],
  .dp-page .resources-grid[style] > [class*="card"],
  .dp-page .next-steps-grid[style] > [class*="card"],
  .dp-page .hero-next-grid[style] > [class*="card"],
  .dp-page .dp-resource-grid[style] > [class*="card"],
  .dp-page .dp-resources-curated-grid[style] > [class*="card"],
  .dp-page .youth-paths-grid[style] > [class*="card"],
  .dp-page .youth-resources-grid[style] > [class*="card"],
  .dp-page .youth-next-steps-grid[style] > [class*="card"] {
    min-height: 0 !important;
  }
  .dp-page .themes-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .resources-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .next-steps-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .hero-next-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .dp-resource-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .dp-resources-curated-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .youth-paths-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .youth-resources-grid[style] > [class*="card"] > .mono:last-child,
  .dp-page .youth-next-steps-grid[style] > [class*="card"] > .mono:last-child {
    margin-top: 12px !important;
    padding-top: 0 !important;
  }

  /* Sticky elements stack inline on mobile. */
  .dp-page [style*="position: sticky"] {
    position: static !important;
  }

  /* Section-head splits collapse to stacked. */
  .section-head,
  .section-head-split {
    grid-template-columns: 1fr !important;
    align-items: start !important;
    gap: 18px !important;
    margin-bottom: 34px !important;
  }
  .section-head-action {
    justify-self: start !important;
  }

  /* "Watch and listen" cinematic plate. On desktop the caption plate
     floats over the bottom-left of the hero; on a phone the 21:9 strip
     is far shorter than the plate's content, so a floated plate overruns
     the image upward and collides with the section header. Give the strip
     a little more height and detach the plate to a static card that peeks
     up just under it. */
  .dp-page .dp-cinematic-hero .photo {
    aspect-ratio: 16 / 10 !important;
  }
  .dp-page .dp-cinematic-hero-plate {
    /* Relative (not static) + z-index so the card's paper background and
       border paint ABOVE the positioned .photo image. As a static
       element it sat behind the image, which hid its background and left
       the text floating illegibly over the photo. The negative top pulls
       it up to overlay the lower portion of the strip (like the desktop
       float), not just peek under its edge. */
    position: relative !important;
    z-index: 2 !important;
    max-width: none !important;
    left: auto !important;
    bottom: auto !important;
    margin: -92px 14px 0 !important;
  }

  /* Hero typography + media tightens. */
  .home-hero-grid {
    grid-template-columns: 1fr !important;
  }
  .home-hero h1 {
    font-size: clamp(42px, 9vw, 60px) !important;
    line-height: 1.02 !important;
  }
  .section-head h2,
  .section-head-split h2 {
    font-size: clamp(32px, 7vw, 44px) !important;
  }
  /* Hero-teaching photo moves below the copy on mobile so the eyebrow
     leads the visual hierarchy. */
  .hero-teaching .hero-media {
    order: 2;
  }
  .hero-teaching-photo {
    height: auto !important;
    min-height: 360px !important;
    aspect-ratio: 4 / 3 !important;
  }

  /* CTA rows wrap; buttons keep 48px touch target. */
  .cta-row {
    flex-wrap: wrap !important;
    gap: 12px !important;
  }
  .cta-row .btn {
    min-height: 48px;
  }

  /* Resource-grid featured (new variant) — image stacks above text. */
  .resources-featured {
    grid-template-columns: 1fr !important;
    gap: 28px !important;
  }
  .resources-type-chips {
    gap: 8px !important;
  }

  /* Footer bottom strip wraps. */
  .footer-bottom,
  .footer-legal,
  .footer-colophon-meta {
    align-items: flex-start !important;
    flex-direction: column !important;
    gap: 12px !important;
  }
  .footer-bottom {
    justify-content: flex-start !important;
  }

  /* Mobile-aspect override for art-directed Photo / FramedPhoto. Photo
     partials that set `data-mobile-aspect="true"` + a `--mobile-aspect`
     CSS var get a different aspect on phones. */
  .photo[data-mobile-aspect="true"] {
    aspect-ratio: var(--mobile-aspect) !important;
  }

  /* Detail-page responsive collapses. Inline-styled multi-col grids in
   * detail partials follow the same `.dp-page` collapse rule above, but
   * a few need bespoke layout overrides on mobile. */
  .dp-page .video-detail-title-strip,
  .dp-page .audio-detail-title-strip {
    grid-template-columns: 1fr !important;
    gap: 14px !important;
    margin-bottom: 24px !important;
  }
  .dp-page .video-detail-context,
  .dp-page .density-meta {
    text-align: left !important;
    padding-bottom: 0 !important;
  }
  .dp-page .video-detail-meta-grid {
    grid-template-columns: 1fr !important;
    gap: 28px !important;
  }
  .dp-page .dp-audio-grid {
    grid-template-columns: 1fr !important;
    gap: 28px !important;
  }
  .dp-page .dp-series-hero-grid {
    grid-template-columns: 1fr !important;
    gap: 28px !important;
  }
  .dp-page .dp-prev-next,
  .dp-page .dp-related-grid,
  .dp-page .dp-series-companions-grid {
    grid-template-columns: 1fr !important;
  }
  .dp-page .dp-series-messages-list > li > a.card {
    grid-template-columns: 48px 1fr !important;
    gap: 14px !important;
  }
  .dp-page .dp-series-messages-list > li > a.card > .photo,
  .dp-page .dp-series-messages-list > li > a.card > [aria-hidden="true"]:last-child {
    display: none !important;
  }
  .dp-page .dp-error-grid {
    grid-template-columns: 1fr !important;
    gap: 40px !important;
  }
  .dp-page .dp-error-sidebar {
    position: static !important;
  }
  .dp-page .dp-listing-hero-grid {
    grid-template-columns: 1fr !important;
    gap: 24px !important;
    align-items: start !important;
  }
  .dp-page .dp-listing-hero-title {
    /* The wordmark gets enormous at desktop; cap it on phones so it
     * doesn't overflow the viewport. */
    font-size: clamp(56px, 14vw, 96px) !important;
  }
  /* Breadcrumbs: keep to a single line on phones. The earlier crumbs
   * (Home / Teachings / …) and the `/` separators hold their size; the
   * current-page label — always the last child — shrinks and ellipsizes
   * instead of wrapping to a second line. */
  .dp-page .dp-crumbs {
    flex-wrap: nowrap !important;
    overflow: hidden;
  }
  .dp-page .dp-crumbs > * {
    flex: 0 0 auto;
    white-space: nowrap;
  }
  .dp-page .dp-crumbs > span:last-child {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .dp-page .dp-listing-filter-grid {
    flex-wrap: wrap !important;
    gap: 12px !important;
    /* The 40px desktop inset is too much on top of the wrap's own
       mobile padding — pull it back so controls keep thumb room. */
    padding-left: 0 !important;
    padding-right: 0 !important;
    padding-top: 10px !important;
    padding-bottom: 10px !important;
  }
  /* Mobile filter: the facet row collapses behind a `Filters` toggle.
     Row 1 is just [Filters] + the result count; the facet controls
     drop into a full-width stacked panel only when the bar carries
     `.is-filters-open` (toggled by runtime.js's initListingFilter). */
  .dp-page .dp-listing-filter-toggle {
    display: inline-flex !important;
  }
  .dp-page .dp-listing-filter-facets {
    display: none !important;
  }
  .dp-page .dp-listing-filter-bar.is-filters-open .dp-listing-filter-facets {
    display: flex !important;
    flex-direction: column;
    align-items: stretch;
    gap: 18px;
    width: 100%;
    padding-top: 6px;
    padding-bottom: 6px;
  }
  /* Toggle picks up the filled treatment while the panel is open. */
  .dp-page .dp-listing-filter-bar.is-filters-open .dp-listing-filter-toggle {
    background: var(--ink, #1F1B16) !important;
    color: var(--paper, #F1EADB) !important;
    border-color: var(--ink, #1F1B16) !important;
  }
  /* Stacked facets stretch to the panel width. */
  .dp-page .dp-listing-filter-bar.is-filters-open .dp-listing-filter-search,
  .dp-page .dp-listing-filter-bar.is-filters-open .dp-listing-filter-select-wrap,
  .dp-page .dp-listing-filter-bar.is-filters-open .dp-listing-filter-select {
    max-width: none !important;
    width: 100%;
  }
  .dp-page .dp-listing-filter-meta {
    margin-left: auto !important;
    padding-left: 0 !important;
    border-left: 0 !important;
  }
  .dp-page .dp-listing-grid {
    grid-template-columns: 1fr !important;
    gap: 40px !important;
  }
  .dp-page .dp-listing-closing-grid {
    grid-template-columns: 1fr !important;
    gap: 28px !important;
  }
  /* Dark "Find what's next." band: once the grid stacks, the action row
     should hang to the left like the rest of the stacked content rather
     than staying right-justified. */
  .dp-page .dp-listing-closing-dark .dp-listing-closing-cta {
    justify-content: flex-start !important;
  }
  .dp-page .dp-path-hero-grid,
  .dp-page .dp-path-item-grid {
    grid-template-columns: 1fr !important;
    gap: 32px !important;
  }
  .dp-page .dp-path-item {
    padding: 64px 0 !important;
  }
}

/* ─── Small mobile (≤560px) ─────────────────────────────────────────────*/
@media (max-width: 560px) {
  body {
    overflow-x: hidden;
  }
  .wrap,
  .wrap-tight,
  .wrap-text {
    padding-left: 20px;
    padding-right: 20px;
  }
  section {
    padding-top: 56px;
    padding-bottom: 56px;
  }
  .home-hero,
  .hero-teaching,
  .hero-next,
  .hero-formation {
    padding-top: 36px !important;
    padding-bottom: 58px !important;
  }
  /* Wider gaps for stacked editorial blocks. */
  .home-hero-grid,
  .latest-grid,
  .youth-grid,
  .community-grid {
    gap: 34px !important;
  }
  /* Even on small mobile, force the typical grids to 1 column. */
  .hero-next-grid,
  .start-grid,
  .themes-grid,
  .resources-grid,
  .next-steps-grid,
  .youth-card-grid,
  .community-gallery,
  .footer-slim-links,
  .footer-full-directory {
    grid-template-columns: 1fr !important;
  }
  /* Hairline-grid gap stays 1px on stacked cards. */
  .hero-next-grid,
  .themes-grid,
  .resources-grid,
  .next-steps-grid {
    gap: 1px !important;
  }
  /* Community testimony quote: KEEP the quote mark beside the first line of
     the quote (a hanging opening quote — reads "Text…), not stacked above it.
     The broad inline-grid collapse `.dp-page [style*="display: grid"]…` (≈L1870)
     forces grid-template-columns:1fr + gap:min(32px,8vw); match its specificity
     (.dp-page + class + [style]) and come later to hold the 2-column layout
     with a smaller mark. */
  .dp-page .community-quote[style] {
    grid-template-columns: auto 1fr !important;
    column-gap: 14px !important;
    row-gap: 0 !important;
    align-items: start !important;
  }
  .community-quote > .serif {
    font-size: 48px !important;
    line-height: 0.9 !important;
  }
  .footer-slim,
  .footer-full {
    padding-top: 56px !important;
  }
  .footer-newsletter-form {
    grid-template-columns: 1fr !important;
    gap: 10px !important;
  }
}

/* ─── Tiny mobile (≤420px) — narrow phones ─────────────────────────────*/
@media (max-width: 420px) {
  .wrap,
  .wrap-tight,
  .wrap-text {
    padding-left: 18px;
    padding-right: 18px;
  }
  .site-wordmark {
    gap: 10px !important;
  }
  .site-wordmark .mono {
    white-space: nowrap;
    letter-spacing: 0.16em;
  }
  .home-hero h1 {
    font-size: clamp(38px, 11vw, 52px) !important;
    line-height: 1.0 !important;
    letter-spacing: -0.015em !important;
  }
  .section-head h2,
  .section-head-split h2 {
    font-size: clamp(30px, 8.6vw, 40px) !important;
    line-height: 1.05 !important;
  }
  .hero-teaching-photo {
    min-height: 300px !important;
  }
  .closing-cta-actions {
    justify-content: flex-start !important;
  }
}

/* ─── Media-type chip ───────────────────────────────────────────────────
   Shared affordance overlay used by every card surface that shows a
   video/audio thumbnail (sermon_card, featured_card, related_grid). The
   chip's `position: absolute` requires the host element to be
   `position: relative` — every card that includes it already is. Markup
   lives in templates/public/blocks/discipleship_project/_partials/
   media_type_chip.html. */
.dp-media-chip {
  position: absolute; top: 14px; left: 14px;
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--paper, #F1EADB);
  background: rgba(28, 25, 22, 0.55);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  padding: 6px 10px; border-radius: 999px;
  display: inline-flex; align-items: center; gap: 8px;
  z-index: 1;
}

/* Icon-only variant — BBC-style circular badge anchored to the
 * bottom-left of any thumbnail container. Used site-wide on cards
 * whose meta row already carries readable type context (speaker ·
 * duration · date). The icon alone communicates the format; no
 * text needed. Bottom-left placement matches the BBC News card
 * convention — natural read-order is image → badge → title → meta,
 * which is what a scanning eye expects.
 *
 * Paper-on-ink with a soft backdrop blur so the badge reads on any
 * thumbnail content (light, dark, or photo). */
.dp-media-icon {
  position: absolute; bottom: 12px; left: 12px;
  width: 30px; height: 30px;
  border-radius: 999px;
  background: rgba(28, 25, 22, 0.72);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--paper, #F1EADB);
  display: inline-flex; align-items: center; justify-content: center;
  z-index: 1;
}
/* Art-less cinematic cards render the title as a typographic plate anchored
   at the bottom of the tone panel; lift the type icon to the top so it
   doesn't collide with that title. (Image cards keep the icon bottom-left.) */
.dp-cinematic-card .photo:not(.has-image) .dp-media-icon {
  top: 12px;
  bottom: auto;
}

/* ─── Path Explore (path-detail redesign) ───────────────────────────────
   The new /path/<slug> composition: existing hero + a numbered editorial
   list of rows + a closing band + a full-bleed modal reader. Rules below
   live in the `dp-pe-*` namespace so they don't collide with the older
   `dp-path-item-*` rules. Resting state ships inline from the partials;
   these rules handle hover lifts, the numeral colour shift, the modal
   chip active state, and the mobile collapse. */
.dp-pe-row { transition: background-color .15s var(--reveal-ease, ease); }
.dp-pe-row-link { transition: background-color .15s var(--reveal-ease, ease); }
.dp-pe-row:hover { background: rgba(28, 25, 22, 0.022); }
/* Row hover affordance: lift the thumbnail slightly, swap the arrow
 * circle to ink so the row feels actionable. Numeral picks up the
 * accent on hover even when the item is not started. */
.dp-pe-row-link:hover .dp-pe-row-thumb {
  transform: translateY(-2px);
}
.dp-pe-row-thumb {
  transition: transform .35s cubic-bezier(.2,.7,.2,1);
}
.dp-pe-row-link:hover .dp-pe-row-arrow {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}
.dp-pe-row-arrow {
  transition: background .2s var(--reveal-ease), color .2s var(--reveal-ease), border-color .2s var(--reveal-ease);
}
.dp-pe-row-link:hover .dp-pe-row-numeral { color: var(--accent); }
.dp-pe-row-numeral { transition: color .2s var(--reveal-ease, ease); }

/* Video player inside the modal: when we include the canonical
 * `video_player.html` partial, it ships its own `<section
 * class="dp-video-player"> > .wrap` chrome whose `.wrap` adds
 * `padding: 0 40px` (so the standalone /contents/video page sits in
 * the editorial column). Inside the modal slot — which already
 * provides horizontal padding — that doubles the inset and shrinks
 * the video. Zero the partial's wrap padding here so the video
 * fills the slot's content area; the Plyr chrome + 16:9 + object-fit
 * stay exactly as the partial defines them. */
.dp-pe-modal-media .dp-video-player { padding: 0; }
.dp-pe-modal-media .dp-video-player > .wrap { padding: 0; max-width: none; }

/* Modal nav buttons — hover swap to ink. JS already paints the
 * disabled state via aria-disabled; this rule covers the resting hover. */
.dp-pe-modal-navbtn:hover,
.dp-pe-modal-closebtn:hover {
  background: var(--ink);
  color: var(--paper);
}
.dp-pe-modal-navbtn,
.dp-pe-modal-closebtn {
  transition: background .2s var(--reveal-ease), color .2s var(--reveal-ease);
}
.dp-pe-modal-chip:hover { background: rgba(28, 25, 22, 0.035); }
.dp-pe-modal-stripend:hover { background: rgba(28, 25, 22, 0.035); }

/* ─── Mobile collapse for the row grid + modal layout ───────────────── */
@media (max-width: 900px) {
  /* Numeral column was removed from the row (commit "Remove numeral from
   * paths list"); the row is now thumbnail + text (the right rail is
   * hidden below). The old `36px 96px 1fr` grid kept a track for the
   * gone numeral, which squeezed the thumbnail into 36px and crammed the
   * text into 96px. Two tracks now: thumbnail + text. */
  .dp-pe-row-link {
    grid-template-columns: 96px 1fr !important;
    gap: 18px !important;
    padding: 22px 0 !important;
  }
  .dp-pe-row-title { font-size: 22px !important; }
  /* Hide the right rail on narrow viewports — the row is already a
   * tappable link, so the state badge + circle arrow read as redundant
   * chrome. Keep duration via the kicker line instead. */
  .dp-pe-row-right { display: none !important; }
  .dp-pe-closing-grid { grid-template-columns: 1fr !important; gap: 24px !important; }
  .dp-pe-closing-actions { justify-content: flex-start !important; }
  /* Modal: stack the top-strip clusters on small screens so the prev/
   * next buttons don't crowd the title meta. */
  .dp-pe-modal-top { flex-wrap: wrap !important; gap: 12px !important; padding: 12px 16px !important; }
  .dp-pe-modal-top-left { gap: 10px !important; font-size: 11px; }
  .dp-pe-modal-slot { padding: 24px 18px 48px !important; }
}

/* ─── DP-Revisited audio detail port ─────────────────────────────────
 * The audio detail page now renders as vertical bands:
 * title hero → player band (cover-inside) → transcript section
 * (left rail aside + shared card) → series orientation → meta/related.
 * Most styling ships inline from the partials; these rules cover the
 * states JS or hover triggers (active transcript row, hover lifts),
 * the language-chip active state, and the mobile collapse for each
 * band. Tokens reused from the existing palette (--accent / --rule /
 * --paper-warm). The shared transcript card is reused by the
 * path-explore modal so any change here ripples to both surfaces. */

/* Player band — hover swap on skip buttons. The play button + scrub
 * track already have their resting state inline; we just need hover. */
.dp-audio-player-skip {
  transition: background .15s var(--reveal-ease, ease), color .15s var(--reveal-ease, ease), border-color .15s var(--reveal-ease, ease);
}
.dp-audio-player-skip:hover {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

/* Transcript card — active row gets the salmon tint + 3px accent
 * left-border + ink text + accent-deep timestamp + accent jump button.
 *
 * Resting state lives HERE (not inline) so the `.is-active` overrides
 * win the cascade. Earlier attempts shipped the colour + border + weight
 * inline, and the inline shorthand beat the external rules by
 * specificity — the row stayed light grey and the "active strip" never
 * showed. Sizing / layout stays inline since it doesn't swap. */
.dp-audio-transcript-line {
  border-left: 3px solid transparent;
  background: transparent;
  color: var(--ink-soft);
  transition: background-color .15s var(--reveal-ease, ease), border-left-color .15s var(--reveal-ease, ease);
}
.dp-audio-transcript-line:hover {
  background: rgba(28, 25, 22, 0.025);
}
.dp-audio-transcript-line.is-active {
  background: var(--accent-tint);
  border-left-color: var(--accent);
  color: var(--ink);
}
.dp-audio-transcript-time {
  color: var(--ink-faint);
}
.dp-audio-transcript-line.is-active .dp-audio-transcript-time {
  color: var(--accent-deep);
}
.dp-audio-transcript-text {
  color: var(--ink-soft);
  font-weight: 300;
  transition: color .15s var(--reveal-ease, ease), font-weight .15s var(--reveal-ease, ease);
}
.dp-audio-transcript-line.is-active .dp-audio-transcript-text {
  color: var(--ink);
  font-weight: 400;
}
.dp-audio-transcript-jump {
  border: 1px solid var(--rule);
  color: var(--ink-faint);
  transition: border-color .12s var(--reveal-ease, ease), color .12s var(--reveal-ease, ease), transform .12s var(--reveal-ease, ease);
}
.dp-audio-transcript-line.is-active .dp-audio-transcript-jump {
  border-color: var(--accent);
  color: var(--accent);
}
.dp-audio-transcript-jump:hover {
  border-color: var(--ink);
  color: var(--ink);
  transform: scale(1.08);
}

/* DOWNLOAD TRANSCRIPT link — quiet hover swaps the underline to the
 * ink tone so the affordance reads as actionable. */
.dp-audio-transcript-download:hover {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

/* Language chips. Resting colour / background / border live HERE
 * (not inline) so the `.is-active` toggle wins the cascade when JS
 * swaps the active chip on click — inline shorthand beats external
 * rules and the chip would stay un-selected after a swap. Sizing
 * + typography stay inline. */
.dp-audio-transcript-lang-chip {
  background: transparent;
  color: var(--ink-soft);
  border: 1px solid var(--rule);
  transition: background .15s var(--reveal-ease, ease), color .15s var(--reveal-ease, ease), border-color .15s var(--reveal-ease, ease);
}
.dp-audio-transcript-lang-chip:hover:not(.is-active) {
  background: rgba(28, 25, 22, 0.04);
}
.dp-audio-transcript-lang-chip.is-active {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

/* Series orientation strip — quiet card hover, mobile collapse to
 * a single stacked column. */
.dp-series-orientation-card {
  transition: background-color .15s var(--reveal-ease, ease), transform .25s cubic-bezier(.2,.7,.2,1);
}
.dp-series-orientation-card.lift:hover {
  transform: translateY(-2px);
  background: var(--paper-warm, var(--paper));
}
.dp-series-orientation-link:hover {
  color: var(--accent);
}

/* ─── Mobile collapse for the new audio-detail bands ───────────────── */
@media (max-width: 900px) {
  .dp-audio-player { grid-template-columns: 1fr !important; }
  .dp-audio-player-cover { min-height: 200px !important; }
  .dp-audio-player-controls { flex-wrap: wrap !important; gap: 12px !important; }
  .dp-audio-now-playing { flex-basis: 100% !important; order: 5; margin-left: 0 !important; }
  .dp-audio-transcript-grid { grid-template-columns: 1fr !important; gap: 32px !important; }
  .dp-audio-transcript-aside { position: static !important; }
  .dp-series-orientation-grid { grid-template-columns: 1fr !important; gap: 14px !important; }
  .dp-series-orientation-card { align-items: flex-start !important; text-align: left !important; }
}
