/* ============================================================
   HOLY LIGHT — Components
   ============================================================ */

/* ---------- Page curtain (transitions) ---------- */
.curtain {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: var(--base);
  display: grid; place-items: center;
  pointer-events: none;
}
.curtain__mark { width: 54px; height: 54px; opacity: .9; animation: pulse 1.6s var(--ease-inout) infinite; }
@keyframes pulse { 0%,100% { opacity:.45; transform: scale(.94);} 50% { opacity:1; transform: scale(1);} }

/* ---------- Buttons ---------- */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  --btn-bd: var(--line-strong);
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
  font-family: var(--font-display); font-weight: 600; font-size: .95rem;
  letter-spacing: .01em;
  padding: .95rem 1.7rem;
  border-radius: var(--radius-pill);
  background: var(--btn-bg); color: var(--btn-fg);
  border: 1px solid var(--btn-bd);
  overflow: hidden; isolation: isolate;
  transition: transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast), color var(--dur-fast);
  white-space: nowrap;
}
.btn::after {                       /* sheen sweep */
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(120deg, transparent 20%, rgba(255,255,255,.22), transparent 80%);
  transform: translateX(-120%);
  transition: transform .7s var(--ease-out);
}
.btn:hover { transform: translateY(-2px); }
.btn:hover::after { transform: translateX(120%); }
.btn:active { transform: translateY(0); }

.btn--gold  { --btn-bg: var(--accent); --btn-fg: var(--btn-gold-text); --btn-bd: var(--accent);
              box-shadow: 0 8px 30px rgba(200,162,75,.28); }
.btn--gold:hover { --btn-bg: var(--accent-hi); --btn-bd: var(--accent-hi); }
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--text); }
.btn--ghost:hover { --btn-bd: var(--accent); --btn-fg: var(--accent); }
.btn--solid { --btn-bg: var(--surface-2); --btn-bd: var(--line); }
.btn--sm    { padding: .6rem 1.15rem; font-size: .85rem; }

/* ---------- Top strip ---------- */
.topbar {
  background: var(--base-2);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-micro);
  color: var(--text-muted);
  position: relative; z-index: 2;
}
.topbar .wrap-wide { display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; padding-block: .55rem; }
.topbar a:hover { color: var(--accent); }
.topbar__social { display: flex; gap: .55rem; align-items: center; }
.topbar__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  color: var(--text-muted);
  transition: color var(--dur-fast), background var(--dur-fast);
}
.topbar__social a:hover { color: var(--accent); background: rgba(255,255,255,.06); }
.topbar__social svg { width: 15px; height: 15px; display: block; }

/* ---------- Header / nav ---------- */
.header {
  position: sticky; top: 0; z-index: var(--z-nav);
  background: transparent;
  transition: background-color var(--dur) var(--ease-out),
              border-color var(--dur), transform var(--dur) var(--ease-out),
              backdrop-filter var(--dur);
  border-bottom: 1px solid transparent;
}
.header.is-stuck {
  background: var(--header-bg);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom-color: var(--line);
}
/* .header.is-hidden is deliberately gone: the header no longer slides away
   on scroll. Losing the transform is a second benefit -- a transformed
   ancestor becomes the containing block for position:fixed children,
   which is the same class of trap the backdrop-filter set for the menu. */
.header__inner { display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; padding-block: 1rem; }

/* Official logo lock-up */
.brand { display: inline-flex; align-items: center; }
.brand__logo { height: 52px; width: auto; }
.footer .brand__logo { height: 74px; }

.menu { display: flex; align-items: center; gap: 1.6rem; list-style: none; }
.menu > li { position: relative; }
.menu a, .menu__toggle {
  position: relative; display: inline-flex; align-items: center; gap: .32rem;
  font-family: var(--font-display); font-weight: 600;
  font-size: .82rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text); padding-block: .35rem;
  transition: color var(--dur-fast);
}
.menu > li > a::after, .menu__toggle::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 0; height: 1.5px; background: var(--accent);
  transition: width .35s var(--ease-out);
}
.menu a:hover, .menu__toggle:hover { color: var(--accent); }
.menu > li > a:hover::after,
.menu > li > a.is-active::after,
.menu__toggle:hover::after,
.has-sub.is-open .menu__toggle::after { width: 100%; }
.has-sub.is-open .menu__toggle svg { transform: rotate(180deg); }
.menu__toggle svg { transition: transform var(--dur-fast) var(--ease-out); }

/* Dropdown */
.submenu {
  position: absolute; top: calc(100% + .9rem); left: -1rem;
  min-width: 250px; list-style: none;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: .5rem; opacity: 0; visibility: hidden;
  transform: translateY(-8px);
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out), visibility var(--dur-fast);
}
.submenu::before {            /* small purple hairline at the top */
  content: ""; position: absolute; top: 0; left: 1.2rem; right: 1.2rem; height: 2px;
  background: linear-gradient(90deg, var(--purple-glow), var(--accent));
  border-radius: 2px;
}
.has-sub:hover .submenu,
.has-sub.is-open .submenu { opacity: 1; visibility: visible; transform: translateY(0); }
.submenu a {
  display: block; width: 100%; padding: .6rem .8rem; border-radius: var(--radius-sm);
  font-size: .78rem; text-transform: none; letter-spacing: .01em; font-weight: 500;
  color: var(--text-muted);
}
.submenu a:hover { background: var(--surface-2); color: var(--accent); }

.header__cta { display: flex; align-items: center; gap: .6rem; }

/* Hamburger */
.hamburger { display: none; width: 44px; height: 44px; position: relative; }
.hamburger span {
  position: absolute; left: 11px; width: 22px; height: 2px; border-radius: 2px;
  background: var(--text); transition: transform .35s var(--ease-out), opacity .2s;
}
.hamburger span:nth-child(1) { top: 16px; }
.hamburger span:nth-child(2) { top: 21px; }
.hamburger span:nth-child(3) { top: 26px; }
.hamburger.is-open span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.hamburger.is-open span:nth-child(2) { opacity: 0; }
.hamburger.is-open span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* ---------- HERO ---------- */
.hero {
  position: relative; min-height: 100svh;
  display: grid; align-items: center;
  overflow: hidden;
  background: var(--base);
}
.hero__bg { position: absolute; inset: 0; z-index: 0; }
.hero__bg::after { content: ""; position: absolute; inset: 0; background: var(--glow-hero); }
.hero__img,
.hero__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
}
/* Still holds the frame until the video can play.
   The leadership banner is a light-background graphic, so it needs darkening
   and desaturating to sit inside the dark hero rather than blowing it out. */
.hero__img {
  opacity: .42;
  filter: brightness(.5) contrast(1.06) saturate(.8);
  object-position: 68% center;      /* favour the portraits, not the baked-in text */
  transition: opacity 1.2s var(--ease-out);
}
.hero__video { opacity: 0;  transition: opacity 1.2s var(--ease-out); }

/* ...then they cross-fade. The still MUST go to 0 — leaving both at 50%
   blends them into a double exposure. */
.hero__bg.is-video .hero__img { opacity: 0; }
.hero__video.is-ready         { opacity: .5; }
.hero__scrim { position: absolute; inset: 0; background: var(--scrim); z-index: 1; }
/* Embers sit above the scrim so they read as light, below the text.
   Strength is a token — this should whisper, not compete with the headline. */
.hero__embers {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: block; pointer-events: none; z-index: 2;
  opacity: var(--embers-strength, .8);
  /* Softens the points into glowing motes rather than sharp dots */
  filter: blur(var(--embers-blur, 2px));
}
.hero__grain { z-index: 2; }
.hero__grain {
  position: absolute; inset: 0; opacity: .16; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}
/* The hero is already min-height:100svh — this padding was stacking on top
   of a full screen, pushing the scroll cue off the bottom on laptops. */
.hero__inner { position: relative; z-index: 2; padding-block: clamp(3.5rem, 9vh, 6rem); }
.hero__title {
  font-size: var(--fs-hero); line-height: var(--lh-tight);
  letter-spacing: -.045em; font-weight: 700; margin-block: 1.2rem .9rem;
  text-wrap: balance;
}
.hero__title em { font-style: normal; color: var(--accent); }
.hero__sub { font-size: clamp(1.05rem, 1.7vw, 1.3rem); color: var(--text-muted); max-width: 46ch; }
.hero__actions { display: flex; flex-wrap: wrap; gap: .8rem; margin-top: 2.2rem; }

.hero__meta {
  display: flex; flex-wrap: wrap; gap: 2.5rem;
  margin-top: 3.4rem; padding-top: 1.8rem;
  border-top: 1px solid var(--line);
}
.hero__meta div strong {
  display: block; font-family: var(--font-display); font-size: 1.05rem; color: var(--text);
}
.hero__meta div span { font-size: var(--fs-small); color: var(--text-muted); }

.scroll-cue {
  position: absolute; bottom: 2rem; left: 50%; transform: translateX(-50%);
  z-index: 3; display: flex; flex-direction: column; align-items: center; gap: .55rem;
  font-size: var(--fs-micro); letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: var(--text-dim);
}
.scroll-cue__line { width: 1px; height: 46px; background: linear-gradient(var(--accent), transparent); }

/* ---------- Broadcast player ---------- */
.player {
  border-radius: var(--radius-lg); overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.player__bar {
  display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
  padding: .85rem 1.1rem;
  background: linear-gradient(90deg, var(--ember-deep), var(--surface-2));
  border-bottom: 1px solid var(--line);
}
.player__tab {
  display: inline-flex; align-items: center; gap: .4rem;
  font-family: var(--font-display); font-weight: 600; font-size: .8rem;
  color: #E8D9CC; background: rgba(255,255,255,.07);
  border: 1px solid rgba(255,255,255,.14);
  padding: .45rem .95rem; border-radius: var(--radius-pill);
  transition: all var(--dur-fast);
}
.player__tab:hover { color: #fff; background: rgba(255,255,255,.15); }
.player__tab.is-active { background: var(--accent); color: var(--btn-gold-text); border-color: var(--accent); }
.player__note { margin-left: auto; font-size: .78rem; color: #C2B3A6; }

/* Resting state: a label, not a claim.
   This badge was hardcoded red with a blinking dot, so the site announced
   "LIVE" every hour of every day. player.js already tracks the real state and
   puts .is-onair on the tab — the styling simply never used it, which meant
   the one week it mattered looked identical to a Tuesday afternoon. */
.live-badge {
  display: inline-flex; align-items: center; gap: .4rem;
  background: rgba(255,255,255,.10); color: var(--text-muted);
  font-size: .68rem; font-weight: 700;
  letter-spacing: .08em; padding: .2rem .55rem; border-radius: 5px;
  transition: background var(--dur-fast), color var(--dur-fast);
}
.live-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; opacity: .55;
}

/* Actually on air. Only now does it earn the red and the pulse. */
.player__tab.is-onair .live-badge { background: #d92d20; color: #fff; }
.player__tab.is-onair .live-dot   { opacity: 1; animation: blink 1.4s infinite; }
@keyframes blink { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .player__tab.is-onair .live-dot { animation: none; }
}

.player__frame { position: relative; aspect-ratio: 16/9; background: #06040a; cursor: pointer; }
.player__poster {
  position: absolute; inset: 0; display: grid; place-content: center; justify-items: center;
  gap: 1.1rem; text-align: center;
  background:
    radial-gradient(620px 320px at 50% 38%, rgba(243,111,39,.34), transparent 70%),
    linear-gradient(160deg, #2A1710, #0A0705);
}
/* The logo, watermarked behind the play button.
   YouTube's own thumbnail was tried first and dropped: the stream's graphic
   is a purple/blue gradient, and it fought the flame palette every week.
   A fixed mark is quieter, always on-brand, and needs no network request. */
.player__poster::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  /* Filling the frame. Opacity comes down as the size goes up — the same
     30% that read as a faint mark at 420px would be a wall of logo at this
     scale, and the play button still has to be the first thing seen. */
  background: url("../img/logo-dark.png") center 42% / min(88%, 880px) auto no-repeat;
  opacity: .22;
}
.player__poster > * { position: relative; z-index: 1; }
.player__date { color: var(--accent); font-weight: 600; }

.player__play {
  width: 84px; height: 84px; border-radius: 50%; background: var(--accent);
  display: grid; place-items: center; box-shadow: 0 12px 40px rgba(249,155,39,.42);
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast);
}
.player__frame:hover .player__play { transform: scale(1.08); background: var(--accent-hi); }
.player__label { font-family: var(--font-display); font-weight: 600; color: #fff; }
.player__small { font-size: .84rem; color: #D9C8BA; }
.player__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* Off-air state — shown instead of YouTube's "this live event has ended" */
.player__poster--offair { cursor: default; gap: 1.3rem; padding: 1.5rem; }
.player__offair-icon {
  width: 62px; height: 62px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--accent);
  border: 1px solid rgba(255,255,255,.16);
  background: rgba(255,255,255,.05);
}
.player__offair-actions {
  display: flex; gap: .6rem; flex-wrap: wrap; justify-content: center;
}
/* Live tab gets a gentle nudge when a service actually starts */
.player__tab.is-onair { box-shadow: 0 0 0 3px rgba(217,45,32,.28); }
.player__foot {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  flex-wrap: wrap; padding: .85rem 1.1rem;
  background: var(--surface-2); font-size: .84rem; color: var(--text-muted);
  border-top: 1px solid var(--line);
}
.player__foot a { color: var(--accent); font-family: var(--font-display); font-weight: 600; }
.player__foot a:hover { color: var(--accent-hi); }

/* ---------- Giving bar ---------- */
.give {
  margin-top: 1.4rem; padding: 1.6rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.give__head { text-align: center; margin-bottom: 1.3rem; }
.give__head h3 { font-size: 1.35rem; }
.give__head p { color: var(--text-muted); font-size: .92rem; margin-top: .25rem; }
.give__grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: .7rem; }
.give__grid--3 { grid-template-columns: repeat(3, 1fr); max-width: 620px; margin-inline: auto; }
.give__grid--5 { grid-template-columns: repeat(5, 1fr); gap: 1rem; }

/* Larger cards for the dedicated Give page */
.give__btn--lg { padding: 1.7rem 1rem 1.5rem; gap: .55rem; }
.give__btn--lg .give__icon { width: 46px; height: 46px; font-size: .95rem; border-radius: 13px; }
.give__btn--lg b { font-size: 1rem; }
.give__btn--lg small { font-size: .78rem; line-height: 1.4; color: var(--accent); }
.give__note-sm {
  font-size: .72rem; color: var(--text-dim); line-height: 1.45; margin-top: .35rem;
}
/* "By Mail" has no link, so it must not look clickable */
.give__btn--static { cursor: default; }
.give__btn--static:hover { transform: none; border-color: var(--line); box-shadow: none; }

/* Zelle: copied, not linked — you type the address into your own bank app */
.give__btn--copy { cursor: pointer; }
.give__copy {
  margin-top: .5rem; font-size: .68rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent);
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  padding: .28rem .7rem; transition: all var(--dur-fast);
}
.give__btn--copy:hover .give__copy { border-color: var(--accent); }
.give__btn--copy.is-copied { border-color: var(--accent); }
.give__btn--copy.is-copied .give__copy {
  background: var(--accent); color: var(--btn-gold-text); border-color: var(--accent);
}

@media (max-width: 1000px) { .give__grid--5 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 620px)  { .give__grid--5 { grid-template-columns: repeat(2, 1fr); } }
.give__btn {
  display: flex; flex-direction: column; align-items: center; gap: .45rem;
  padding: 1.05rem .5rem; text-align: center;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius); transition: all var(--dur-fast) var(--ease-out);
}
.give__btn:hover {
  border-color: var(--accent); transform: translateY(-4px);
  box-shadow: 0 12px 30px rgba(0,0,0,.3);
}
.give__icon {
  width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center;
  font-family: var(--font-display); font-weight: 700; font-size: .82rem; color: #fff;
}
/* White tile behind each brand mark. These logos were drawn to sit on white —
   Zelle's purple and PayPal's navy both disappear against our near-black, and
   recolouring someone else's trademark is not ours to do. */
.give__icon--logo { background: #fff; padding: 5px; }
.give__icon--logo img { width: 100%; height: 100%; object-fit: contain; display: block; }
.give__btn b { font-family: var(--font-display); font-size: .87rem; color: var(--text); }
.give__btn small { font-size: .7rem; color: var(--text-muted); }
.give__note { text-align: center; margin-top: 1.1rem; font-size: .82rem; color: var(--text-muted); }
.give__note b { color: var(--accent); }

/* ---------- Sticky scroll story ---------- */
.story { position: relative; }
.story__sticky {
  position: sticky; top: 0; height: 100svh;
  display: grid; place-items: center; overflow: hidden;
}
/* Each panel MUST be opaque: they are consecutive sticky siblings that pin at
   the same offset, so a transparent panel lets the previous one show through
   and the headlines visibly overlap. */
.story__media {
  position: absolute; inset: 0;
  background: var(--base);
}
/* These are generated stand-ins, not real photographs of the congregation.
   A slight blur keeps them atmospheric and stops them reading as documentary.
   The scale-up hides the soft/transparent edge that blur creates.
   Tune with --story-blur; set it to 0 once real photos are dropped in. */
.story__media img, .story__media video {
  width: 100%; height: 100%; object-fit: cover; opacity: .32;
  filter: blur(var(--story-blur, 14px));
  /* scale must grow with the blur, or the softened edge shows as a seam */
  transform: scale(var(--story-blur-scale, 1.14));
}
.story__media::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(10,7,5,.55), rgba(10,7,5,.88)); }
/* Subtle tint per panel so the stack still reads as movement without photos */
.story__sticky:nth-child(1) .story__media { background:
  radial-gradient(900px 600px at 70% 20%, rgba(249,155,39,.10), transparent 65%), var(--base); }
.story__sticky:nth-child(2) .story__media { background:
  radial-gradient(900px 600px at 25% 70%, rgba(106,71,184,.14), transparent 65%), var(--base); }
.story__sticky:nth-child(3) .story__media { background:
  radial-gradient(900px 600px at 50% 40%, rgba(240,72,40,.12), transparent 65%), var(--base); }
.story__content { position: relative; z-index: 2; text-align: center; max-width: 44ch; }
/* Promo panels carry a CTA and a slightly stronger scrim, since event
   artwork is usually busier than the evergreen imagery. */
.story__sticky--promo .story__media::after {
  background: linear-gradient(180deg, rgba(10,7,5,.62), rgba(10,7,5,.92));
}
.story__cta { margin-top: .4rem; }
.story__num {
  font-family: var(--font-display); font-size: .78rem; letter-spacing: var(--tracking-wide);
  color: var(--accent); margin-bottom: 1rem;
  display: inline-flex; align-items: center; gap: .7rem;
}
.story__num::before {          /* small purple tick */
  content: ""; width: 22px; height: 2px; border-radius: 2px;
  background: var(--purple-glow);
}

/* ---------- Cards ---------- */
.cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.15rem; }
.card {
  position: relative; padding: 1.9rem 1.5rem 1.6rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  transition: transform var(--dur) var(--ease-out), border-color var(--dur);
}
/* Each card carries the header of the page it leads to, so the tile is a
   glimpse of the destination rather than four identical dark rectangles.
   Kept dim: this is a backdrop for the words, and a card you cannot read is
   worse than a card with nothing behind it. It lifts on hover, which is the
   only moment the picture is what you are looking at. */
.card--bg { padding-top: 2.2rem; }
.card__bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: .20; filter: saturate(.85);
  transition: opacity var(--dur) var(--ease-out), transform 1.1s var(--ease-out);
}
.card--bg:hover .card__bg { opacity: .34; transform: scale(1.06); }
/* Weighted to the bottom, where the text sits. */
.card--bg::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(21,16,12,.55) 0%, rgba(21,16,12,.80) 45%, rgba(21,16,12,.95) 100%);
}
.card--bg > *:not(.card__bg) { position: relative; z-index: 2; }

.card::before {
  content: ""; position: absolute; inset: 0;
  background: var(--glow-soft); opacity: 0;
  transition: opacity var(--dur) var(--ease-out); pointer-events: none;
}
.card:hover { transform: translateY(-8px); border-color: var(--line-strong); }
.card:hover::before { opacity: 1; }
/* Ember → purple: the small cool note that makes the flame icon read hotter */
.card__icon {
  width: 52px; height: 52px; border-radius: 15px; display: grid; place-items: center;
  background: linear-gradient(150deg, var(--ember) 0%, var(--purple-deep) 100%);
  border: 1px solid rgba(255,255,255,.10); margin-bottom: 1.15rem;
  transition: background var(--dur) var(--ease-out);
}
.card:hover .card__icon {
  background: linear-gradient(150deg, var(--ember) 0%, var(--purple) 100%);
}
.card h3 { font-size: 1.18rem; margin-bottom: .45rem; }
.card p  { font-size: .93rem; color: var(--text-muted); margin-bottom: 1.05rem; }
.card__more {
  font-family: var(--font-display); font-weight: 600; font-size: .87rem; color: var(--accent);
  display: inline-flex; align-items: center; gap: .4rem;
}
.card__more span { transition: transform var(--dur-fast) var(--ease-out); }
.card:hover .card__more span { transform: translateX(5px); }

/* ---------- Stat strip ---------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; text-align: center; }
.stat__num {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(2rem, 4.5vw, 3.4rem); color: var(--accent);
  letter-spacing: -.03em; line-height: 1;
}
.stat__label { font-size: var(--fs-small); color: var(--text-muted); margin-top: .5rem; }

/* ---------- Wireframe city ("Find Us") ---------- */
.city-section {
  position: relative; overflow: hidden;
  min-height: 100svh; display: grid; align-items: center;
  background: var(--base);
}
.city { position: absolute; inset: 0; }
.city canvas,
.city__video { width: 100%; height: 100%; display: block; object-fit: cover; }

/* Satellite imagery is bright and busy — light text needs real help.
   Darkening the footage itself does more for legibility than any overlay,
   and it keeps the section consistent with the rest of the dark theme. */
.city__video { filter: brightness(var(--city-dim, .45)) contrast(1.08) saturate(.85); }

.city::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    /* heavy on the left where the copy sits, easing off over the church */
    linear-gradient(90deg,
      rgba(10,7,5,.96) 0%,
      rgba(10,7,5,.88) 30%,
      rgba(10,7,5,.55) 55%,
      rgba(10,7,5,.35) 75%,
      rgba(10,7,5,.6)  100%),
    /* vignette to pull the eye inward */
    radial-gradient(75% 70% at 60% 50%, transparent 25%, rgba(10,7,5,.65) 100%);
}
.city--fallback canvas { display: none; }
.city--fallback::before {            /* mobile / reduced-motion: real aerial */
  content: ""; position: absolute; inset: 0;
  background: url("../img/portsmouth.jpg") center/cover no-repeat;
  opacity: .55;
}
.city-section__content {
  position: relative; z-index: 2; max-width: 34ch;
  /* insurance: keeps text readable even if a bright frame slips through */
  text-shadow: 0 2px 18px rgba(0,0,0,.85), 0 1px 3px rgba(0,0,0,.6);
}
.city-section__addr {
  font-family: var(--font-display); font-size: 1.05rem;
  color: var(--text); margin-top: 1.2rem; line-height: 1.6;
}
.city-section__hours {
  margin-top: 1.4rem; padding-top: 1.2rem; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: .55rem;
  font-size: var(--fs-small); color: var(--text-muted);
}
.city-section__hours b { color: var(--text); font-family: var(--font-display); font-weight: 600; }
/* Imagery attribution — required by Google's Geo Guidelines.
   Must stay near the content, not relegated to the page footer. */
.city-attrib {
  position: absolute; left: 0; right: 0; bottom: 1rem; z-index: 3;
  text-align: center; font-size: .66rem; letter-spacing: .04em;
  color: rgba(255,255,255,.5); pointer-events: none;
}

.city-pin {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: var(--fs-micro); letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: var(--accent);
}
.city-pin::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 4px rgba(249,155,39,.22);
  animation: pinPulse 2.2s var(--ease-inout) infinite;
}
@keyframes pinPulse {
  0%,100% { box-shadow: 0 0 0 4px rgba(249,155,39,.22); }
  50%     { box-shadow: 0 0 0 10px rgba(249,155,39,0); }
}
@media (max-width: 900px) {
  /* Copy is full-width here, so the scrim has to run vertically instead. */
  .city::after {
    background: linear-gradient(180deg,
      rgba(10,7,5,.55) 0%, rgba(10,7,5,.8) 45%, rgba(10,7,5,.95) 100%);
  }
  .city-section__content { max-width: none; }
}

/* ---------- Inner-page header ---------- */
.page-hero {
  position: relative; overflow: hidden;
  padding-block: clamp(4.5rem, 12vh, 8rem) clamp(3rem, 7vh, 5rem);
  background: var(--base);
  border-bottom: 1px solid var(--line);
  text-align: center;
}
.page-hero::before {
  content: ""; position: absolute; inset: 0;
  background: var(--glow-hero); opacity: .8; pointer-events: none;
}
.page-hero::after {                 /* faint grain, same as the home hero */
  content: ""; position: absolute; inset: 0; opacity: .12; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
  pointer-events: none;
}
.page-hero__inner { position: relative; z-index: 1; }
.page-hero h1 {
  font-size: clamp(2rem, 5vw, 3.6rem);
  margin-top: .8rem; text-wrap: balance;
}
.page-hero__sub {
  margin: 1.1rem auto 0; max-width: 56ch;
  color: var(--text-muted); font-size: clamp(1rem, 1.4vw, 1.12rem);
}

/* ---------- Inner-page header, photographic variant ----------
   The photographs are the old Gantry page headers, about 750px wide, so they
   are upscaled roughly 2x. The scrim earns its keep twice over: it buys
   contrast for the type, and it hides the softness. Do not lighten it much. */
.page-hero--banner {
  padding-block: clamp(6rem, 17vh, 10.5rem) clamp(4rem, 9vh, 6.5rem);
  text-align: left;
}
.page-hero__bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center 40%;
}
/* Above the photo, below the text. Weighted left so the heading always has
   ground under it while the right of the picture stays open. */
.page-hero--banner::before {
  z-index: 1;
  background:
    linear-gradient(90deg,
      rgba(10,7,5,.94) 0%, rgba(10,7,5,.86) 32%,
      rgba(10,7,5,.58) 64%, rgba(10,7,5,.42) 100%),
    linear-gradient(to bottom,
      rgba(10,7,5,.72) 0%, rgba(10,7,5,.20) 42%, rgba(10,7,5,.78) 100%);
  opacity: 1;
}
.page-hero--banner::after { z-index: 2; }
.page-hero--banner .page-hero__inner { z-index: 3; }
.page-hero--banner .page-hero__sub { margin-inline: 0; }

@media (max-width: 700px) {
  /* Narrow screens crop most of the picture away, so the gradient turns
     vertical — otherwise the heading lands on the one bright patch left. */
  .page-hero--banner::before {
    background:
      linear-gradient(to bottom,
        rgba(10,7,5,.66) 0%, rgba(10,7,5,.30) 38%, rgba(10,7,5,.92) 100%);
  }
  .page-hero__bg { object-position: center 32%; }
}


/* ---------- Live bar (every page, while a service is on) ---------- */
.livebar {
  position: sticky; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  gap: .7rem; flex-wrap: wrap;
  padding: .6rem 1rem;
  background: linear-gradient(90deg, #B3231A, #F04828 55%, #F26428);
  color: #fff; font-size: .88rem; text-align: center;
}
.livebar strong { font-family: var(--font-display); letter-spacing: .04em; }
.livebar__name { opacity: .92; }
.livebar__dot {
  width: 9px; height: 9px; border-radius: 50%; background: #fff;
  box-shadow: 0 0 0 0 rgba(255,255,255,.7);
  animation: livepulse 1.9s ease-out infinite;
}
@keyframes livepulse {
  70%  { box-shadow: 0 0 0 9px rgba(255,255,255,0); }
  100% { box-shadow: 0 0 0 0 rgba(255,255,255,0); }
}
/* Amber rather than red before it starts — red says "happening now", and
   claiming that five minutes early is the sort of small lie people notice. */
.livebar--soon { background: linear-gradient(90deg, #8A4A12, #C4611A 55%, #F99B27); }
.livebar--soon .livebar__dot { animation: none; opacity: .85; }
.livebar__cta {
  background: rgba(0,0,0,.28); border: 1px solid rgba(255,255,255,.4);
  padding: .28rem .8rem; border-radius: var(--radius-pill);
  font-weight: 600; font-size: .82rem; color: #fff;
}
.livebar__cta:hover { background: #fff; color: #B3231A; }
@media (prefers-reduced-motion: reduce) { .livebar__dot { animation: none; } }

/* ---------- Live hero (home page) ---------- */
.livehero {
  background: #000; border-bottom: 1px solid var(--line);
  padding: clamp(1rem, 3vh, 2.5rem) 0 clamp(1.5rem, 4vh, 3rem);
}
.livehero__stage {
  width: min(1280px, 94vw); margin-inline: auto;
  aspect-ratio: 16 / 9; position: relative;
  border-radius: 14px; overflow: hidden;
  background: linear-gradient(160deg, #2A1710, #0A0705);
  display: grid; place-items: center;
}
.livehero__stage iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
.livehero__soon { text-align: center; padding: 1.5rem; }
.livehero__kicker {
  font-family: var(--font-display); color: var(--accent);
  letter-spacing: .12em; text-transform: uppercase; font-size: .82rem;
}
.livehero__count {
  font-family: var(--font-display); font-weight: 700; line-height: 1;
  font-size: clamp(4rem, 14vw, 8rem); color: var(--text); margin: .5rem 0 .2rem;
}
.livehero__unit { color: var(--text-muted); font-size: .95rem; }
/* Used on Facebook nights in place of the countdown number, so the hero still
   reads as "something is happening now" without a number to show. */
/* Facebook blue, not the site palette. A platform mark is a wayfinding
   signal — recolouring it to match the page makes it stop working as one. */
.livehero__fb { margin: 0 0 .6rem; color: #1877F2; line-height: 0; }
.livehero__fb svg { display: inline-block; }
.livehero__live {
  margin: 0 0 .35rem; font-family: var(--font-display), serif;
  font-size: clamp(1.6rem, 4vw, 2.6rem); line-height: 1.1; color: #fff;
}
.livehero__note { margin-top: 1.4rem; font-size: .86rem; color: var(--text-muted); }
.livehero__note a { color: var(--accent); text-decoration: underline; }
/* Sits below the iframe, not over it — the whole point is that it stays
   reachable when YouTube is showing an error we cannot see or restyle. */
.livehero__note--under {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  margin: 0; padding: .6rem 1rem; text-align: center;
  background: linear-gradient(180deg, transparent, rgba(0,0,0,.82) 45%);
  color: rgba(255,255,255,.9);
}
.livehero__note--under .link { color: var(--accent); text-decoration: underline; }

/* ---------- Floating social ---------- */
.social-float {
  position: fixed; left: 50%; transform: translateX(-50%) translateY(14px);
  bottom: clamp(.9rem, 3vh, 1.6rem); z-index: 55;
  display: flex; gap: .35rem; padding: .4rem;
  background: rgba(22,16,12,.82); backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  box-shadow: 0 12px 34px rgba(0,0,0,.5);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .4s var(--ease-out), transform .4s var(--ease-out), visibility .4s;
}
.social-float.is-shown {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translateX(-50%) translateY(0);
}
.social-float a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%;
  transition: color var(--dur-fast), background var(--dur-fast), transform var(--dur-fast);
}
.social-float a:hover { background: rgba(255,255,255,.10); transform: translateY(-2px); }
.social-float svg { width: 17px; height: 17px; display: block; }

.footer__social { display: flex; gap: .5rem; margin-bottom: .4rem; }
.footer__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 50%;
  border: 1px solid var(--line);
  transition: border-color var(--dur-fast), background var(--dur-fast);
}
.footer__social a:hover { background: rgba(255,255,255,.06); }
.footer__social svg { width: 17px; height: 17px; display: block; }

/* ---------- Social brand colours ----------
   The icons draw with currentColor, so setting colour on the anchor is all
   it takes. Selected by aria-label rather than position, so reordering the
   markup cannot quietly turn Facebook red.

   These are the platforms' own colours, not ours. That is the point: on a
   page this deliberately monotone, three small marks of real colour read as
   "these are other places we live" rather than as decoration. */
.footer__social a[aria-label="Facebook"],
.social-float  a[aria-label="Facebook"]  { color: #4A9BFF; }
.footer__social a[aria-label="Instagram"],
.social-float  a[aria-label="Instagram"] { color: #F0568F; }
.footer__social a[aria-label="YouTube"],
.social-float  a[aria-label="YouTube"]   { color: #FF4B45; }

/* Lifted off the true brand values — #1877F2 and #FF0000 sit too dark against
   #0A0705 to read at 17px. These are the same hues, raised until they do. */
.footer__social a[aria-label="Facebook"]:hover,
.social-float  a[aria-label="Facebook"]:hover  { color: #7FBAFF; border-color: #4A9BFF; }
.footer__social a[aria-label="Instagram"]:hover,
.social-float  a[aria-label="Instagram"]:hover { color: #FF7FAC; border-color: #F0568F; }
.footer__social a[aria-label="YouTube"]:hover,
.social-float  a[aria-label="YouTube"]:hover   { color: #FF7A75; border-color: #FF4B45; }

/* ---------- Promo panels (a featured event takes over a story panel) ----------
   The story panels blur their imagery to 32% opacity because those are
   atmospheric stand-ins — background, not content. A flyer is the opposite:
   it is the message, the church designed it, and every one of these overrides
   exists to undo a treatment that would bury it. */
.story__sticky--promo .story__media img {
  object-fit: contain;      /* whole flyer, never cropped */
  opacity: 1;               /* was .32 */
  filter: none;             /* was blur(14px) */
  transform: none;          /* was scale(1.14), which existed to hide blur edges */
  padding: clamp(2rem, 6vh, 4rem) 1rem clamp(11rem, 26vh, 15rem);
}
/* Scrim only under the text, so the flyer keeps its colour. */
.story__sticky--promo .story__media::after {
  background: linear-gradient(180deg,
    rgba(10,7,5,.30) 0%, rgba(10,7,5,0) 22%,
    rgba(10,7,5,0) 46%, rgba(10,7,5,.90) 78%, rgba(10,7,5,.97) 100%);
}
.story__sticky--promo .story__content {
  align-self: end; padding-bottom: clamp(2.5rem, 7vh, 4.5rem); text-align: center;
}
.story__sticky--promo .story__content h2 { text-wrap: balance; }

@media (min-width: 900px) {
  /* Wide screens: a 9:16 flyer contained in a 16:9 panel leaves a lot of empty
     ground either side. Put the flyer on the right and the words on the left
     and that space becomes the layout instead of a gap. */
  .story__sticky--promo .story__media img {
    object-position: 76% center;
    padding: clamp(2rem, 7vh, 5rem) 2rem;
  }
  .story__sticky--promo .story__media::after {
    background: linear-gradient(90deg,
      rgba(10,7,5,.94) 0%, rgba(10,7,5,.86) 30%,
      rgba(10,7,5,.30) 52%, rgba(10,7,5,0) 68%);
  }
  .story__sticky--promo .story__content {
    align-self: center; text-align: left;
    max-width: 46%; margin-right: auto; padding-bottom: 0;
  }
}

/* ---------- Holding page (HLCB, Store) ----------
   An "under construction" page still owes the visitor an answer. The phone
   number is the point of the page, so it is the largest thing on it after
   the heading — not a footnote. */
.holding { max-width: 62ch; margin-inline: auto; text-align: center; }
.holding h2 {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 3vw, 2rem); margin-bottom: 1.2rem;
}
.holding p { color: var(--text-muted); margin-bottom: 1.1rem; line-height: 1.7; }
.holding__seal { width: min(190px, 42vw); height: auto; margin: 0 auto 2rem; display: block; }
.holding__mark { color: var(--accent); font-size: 2rem; margin-bottom: 1.4rem; }
.holding__cta {
  margin-top: 2.6rem; padding: 2rem 1.5rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
}
.holding__lead { color: var(--text); margin-bottom: 1.3rem; }
.holding__cta .btn { font-size: 1.15rem; padding: 1rem 2.2rem; }

/* ---------- Events list ----------
   Cards are portrait because the flyers are. The church admin builds them at
   9:16, and a 16:9 card would crop the top and bottom off every one — which
   on a flyer is usually the date and the phone number. */
.evlist { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.4rem;
          grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); }

.evcard {
  display: flex; flex-direction: column; height: 100%;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; overflow: hidden;
}
.evcard:hover { border-color: var(--accent); }
.evcard__img { display: block; aspect-ratio: 9 / 16; background: var(--surface-2);
               overflow: hidden; position: relative; }
/* contain, not cover: a flyer is information, and cropping it loses some.
   The tinted backdrop fills whatever the aspect does not. */
.evcard__img img { width: 100%; height: 100%; object-fit: contain; display: block;
                   transition: transform .7s var(--ease-out); }
.evcard:hover .evcard__img img { transform: scale(1.03); }

/* No banner? Show the date instead — a card with an empty grey box on it
   reads as broken, a card with a date reads as deliberate. */
.evcard__date {
  position: absolute; inset: 0; display: grid; place-content: center; text-align: center;
  background: linear-gradient(150deg, var(--surface-3), var(--surface));
}
.evcard__date strong { font-family: var(--font-display); font-size: 2.6rem;
                       color: var(--accent); line-height: 1; display: block; }
.evcard__date small { font-size: .82rem; letter-spacing: .14em; text-transform: uppercase;
                      color: var(--text-muted); }

.evcard__meta { display: block; padding: 1.1rem 1.25rem 1.3rem; }
.evcard__cat { font-size: .7rem; letter-spacing: .12em; text-transform: uppercase;
               color: var(--accent); display: block; margin-bottom: .35rem; }
.evcard__meta strong { font-family: var(--font-display); font-size: 1.16rem;
                       display: block; margin-bottom: .3rem; text-wrap: balance; }
.evcard__meta small { color: var(--text-muted); font-size: .87rem; }
.evcard__file { display: block; margin-top: .6rem; font-size: .8rem; color: var(--text-dim); }

.evlist--past { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .8rem; }
.evlist--past .evcard__meta { padding: .9rem 1.1rem; }
.evlist--past .evcard__meta strong { font-size: 1rem; }

/* ---------- One event ---------- */
.ev { display: grid; gap: 2rem; grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr); }
.ev__banner { grid-column: 1; border-radius: 14px; overflow: hidden;
              border: 1px solid var(--line); background: var(--surface);
              display: grid; place-items: center; }
/* Shown whole, never cropped — this is the flyer, and every corner of it is
   something the church meant to say. Capped by height so a 9:16 flyer does
   not run past a laptop screen and force a scroll to read the bottom. */
.ev__banner img { max-width: 100%; max-height: 78vh; width: auto; height: auto; display: block; }
.ev__side { grid-column: 2; grid-row: 1 / span 2; align-self: start;
            position: sticky; top: 6rem; }
.ev__body { grid-column: 1; max-width: none; }

.ev__facts { background: var(--surface); border: 1px solid var(--line);
             border-radius: 14px; padding: 1.4rem 1.5rem; margin: 0; }
.ev__facts dt { font-size: .72rem; letter-spacing: .12em; text-transform: uppercase;
                color: var(--accent); margin-bottom: .25rem; }
.ev__facts dd { margin: 0 0 1.2rem; color: var(--text); font-size: .96rem; line-height: 1.55; }
.ev__facts dd:last-child { margin-bottom: 0; }
.ev__map { display: block; margin-top: .3rem; color: var(--accent); font-size: .86rem; }

.ev__dl { width: 100%; margin-top: 1rem; justify-content: center; gap: .5rem; }
.ev__dl span { opacity: .75; font-weight: 400; font-size: .84rem; }

@media (max-width: 860px) {
  .ev { grid-template-columns: 1fr; }
  .ev__banner, .ev__side, .ev__body { grid-column: 1; }
  .ev__side { grid-row: auto; position: static; }
}

/* ---------- Photo gallery ---------- */
.albums, .shots { list-style: none; padding: 0; margin: 0; display: grid; }
.albums { gap: 1.6rem; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr)); }
.shots  { gap: .85rem; grid-template-columns: repeat(auto-fill, minmax(212px, 1fr)); }

.album { display: block; border-radius: 14px; overflow: hidden; background: var(--surface); border: 1px solid var(--line); }
.album__img { display: block; aspect-ratio: 4 / 3; background: var(--surface-2); overflow: hidden; }
.album__img img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .7s var(--ease-out);
}
.album:hover { border-color: var(--accent); }
.album:hover .album__img img { transform: scale(1.05); }
.album__meta { display: block; padding: 1rem 1.15rem 1.15rem; }
.album__meta strong { font-family: var(--font-display); font-size: 1.08rem; display: block; }
.album__meta small { color: var(--text-muted); font-size: .85rem; }

/* The tile is a button, not a link — it opens the lightbox rather than
   navigating, and a button is what a keyboard user expects to activate. */
.shot {
  display: block; width: 100%; padding: 0; border: 0; cursor: zoom-in;
  border-radius: 9px; overflow: hidden; background: var(--surface-2);
  aspect-ratio: 1; line-height: 0;
}
.shot img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease-out), opacity .3s; }
.shot:hover img { transform: scale(1.06); opacity: .88; }
.shot:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.shot__cap { display: block; font-size: .8rem; color: var(--text-muted); margin-top: .45rem; }

.link-back { color: var(--accent); font-size: .92rem; }

/* ---------- Lightbox ---------- */
.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(6,4,3,.94);
  display: grid; place-items: center;
  padding: clamp(1rem, 5vw, 3.5rem);
}
.lightbox[hidden] { display: none; }
.lightbox__img {
  max-width: 100%; max-height: 82vh; object-fit: contain;
  border-radius: 6px; box-shadow: 0 30px 90px rgba(0,0,0,.7);
}
.lightbox__cap {
  position: absolute; left: 0; right: 0; bottom: clamp(.8rem, 3vw, 1.8rem);
  text-align: center; color: var(--text-muted); font-size: .9rem; padding-inline: 4rem;
}
.lightbox__close, .lightbox__nav {
  position: absolute; background: rgba(255,255,255,.07); border: 1px solid var(--line);
  color: var(--text); border-radius: 50%; cursor: pointer; line-height: 1;
  display: grid; place-items: center;
}
.lightbox__close { top: 1.1rem; right: 1.1rem; width: 42px; height: 42px; font-size: 1.5rem; }
.lightbox__nav   { top: 50%; transform: translateY(-50%); width: 48px; height: 48px; font-size: 2rem; }
.lightbox__nav--prev { left: clamp(.5rem, 2vw, 1.6rem); }
.lightbox__nav--next { right: clamp(.5rem, 2vw, 1.6rem); }
.lightbox__close:hover, .lightbox__nav:hover { background: var(--accent); color: #241002; border-color: var(--accent); }

@media (max-width: 620px) {
  .shots { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .lightbox__cap { padding-inline: 1rem; }
}

/* ---------- Memorial portrait ----------
   The source already carries its own gold ring with transparent corners, so
   this adds no frame of its own — a second ring would fight the first. It is
   a 300px scan, held to 200px so it stays sharp, and no larger. */
.memorial { margin: 0 auto 2.6rem; text-align: center; }
.memorial img {
  width: min(200px, 52vw); height: auto; display: block; margin: 0 auto;
  filter: drop-shadow(0 14px 34px rgba(0,0,0,.55));
}
.memorial figcaption {
  font-family: var(--font-display); font-size: .96rem;
  color: var(--text-muted); margin-top: 1rem;
}

/* ---------- Tribute: family list and descendant counts ---------- */
.legacy {
  list-style: none; margin: 2.4rem 0 0; padding: 0;
  display: grid; gap: 1px;
  background: var(--line);            /* gap shows through as hairlines */
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(258px, 1fr));
}
.legacy__item {
  background: var(--surface); padding: 1.5rem 1.6rem;
  display: flex; flex-direction: column; gap: .3rem;
}
.legacy__name {
  font-family: var(--font-display); font-size: 1.16rem; color: var(--text);
}
.legacy__role { font-size: .9rem; color: var(--text-muted); line-height: 1.5; }

.legacy__count {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: clamp(1.8rem, 6vw, 4.5rem); margin-top: 3rem; text-align: center;
}
.legacy__count > div { display: flex; flex-direction: column; gap: .2rem; }
.legacy__count strong {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(2.2rem, 5vw, 3.1rem); color: var(--accent); line-height: 1;
}
.legacy__count span {
  font-size: .8rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--text-muted);
}

/* ---------- Long-form article body ---------- */
.prose { max-width: 68ch; margin-inline: auto; }
.prose > * + * { margin-top: 1.15rem; }
.prose p { color: var(--text-muted); }
.prose h2 {
  font-size: clamp(1.3rem, 2.4vw, 1.75rem);
  margin-top: 2.8rem; color: var(--text);
}
.prose h3 { font-size: 1.15rem; margin-top: 2rem; color: var(--text); }
.prose strong { color: var(--text); font-weight: 600; }
/* :not(.btn) matters more than it looks. ".prose a" is specificity (0,1,1)
   and ".btn" is (0,1,0), so without this every button inside a prose block
   loses its own text colour to the accent — which on a gold button is orange
   text on an orange background, i.e. invisible. It was doing exactly that on
   the Church Hours page. */
.prose a:not(.btn) {
  color: var(--accent); text-decoration: underline; text-underline-offset: 3px;
}
.prose a:hover { color: var(--accent-hi); }
.prose ul, .prose ol { padding-left: 1.3rem; color: var(--text-muted); }
.prose li + li { margin-top: .45rem; }
.prose blockquote {
  border-left: 2px solid var(--accent);
  padding: .3rem 0 .3rem 1.3rem; margin-left: 0;
  color: var(--text); font-family: var(--font-display);
  font-size: 1.08rem; line-height: 1.5;
}
.prose hr { border: 0; height: 1px; background: var(--line); margin-block: 2.4rem; }
.prose img { border-radius: var(--radius); margin-inline: auto; }

/* Portrait + intro pairing for the leadership pages */
.bio { display: grid; grid-template-columns: 300px 1fr; gap: 2.6rem; align-items: start; }
.bio__portrait {
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--line); box-shadow: var(--shadow-sm);
  position: sticky; top: 100px;
}
.bio__role {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--accent); margin-top: 1rem; display: block; text-align: center;
}
@media (max-width: 860px) {
  .bio { grid-template-columns: 1fr; gap: 1.8rem; }
  .bio__portrait { position: static; max-width: 320px; margin-inline: auto; }
}

/* ---------- Ministries grid ---------- */
.min-group { margin-top: 2.6rem; }
.min-group h2 {
  font-size: 1.05rem; letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: var(--accent);
  padding-bottom: .7rem; border-bottom: 1px solid var(--line);
}
.min-list {
  list-style: none; padding: 0; margin-top: 1.1rem;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: .6rem 1.6rem;
}
.min-list li {
  color: var(--text-muted); font-size: .95rem;
  padding-left: 1.1rem; position: relative;
}
.min-list li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent); opacity: .65;
}

/* ---------- Service times ---------- */
.times { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.1rem; }
.times__card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 1.7rem 1.5rem;
}
.times__card h2 {
  font-size: .72rem; letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: var(--accent); margin-bottom: 1rem;
}
.times__row { display: flex; gap: .9rem; align-items: baseline; }
.times__row + .times__row { margin-top: .8rem; padding-top: .8rem; border-top: 1px solid var(--line); }
.times__when {
  font-family: var(--font-display); font-weight: 600; color: var(--text);
  white-space: nowrap; min-width: 5.4rem;
}
.times__what { color: var(--text-muted); font-size: .95rem; }

/* ---------- Service countdown ---------- */
.service {
  position: relative; overflow: hidden;
  background: var(--base-2);
  border-block: 1px solid var(--line);
  padding-block: 1.5rem;
}
.service::before {
  content: ""; position: absolute; inset: 0;
  background: var(--glow-soft); opacity: .5; pointer-events: none;
}
.service__inner {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem 2rem; flex-wrap: wrap;
}

.service__status { display: flex; align-items: center; gap: .85rem; }
.service__dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-dim); flex: 0 0 auto;
  transition: background var(--dur-fast);
}
.service__label {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--accent);
}
.service__name { font-size: .92rem; color: var(--text-muted); margin-top: .15rem; }

/* Live state — the dot pulses red and the label turns urgent */
.service.is-live .service__dot {
  background: #d92d20;
  animation: servicePulse 1.6s var(--ease-inout) infinite;
}
.service.is-live .service__label { color: #ff6a5c; }
@keyframes servicePulse {
  0%,100% { box-shadow: 0 0 0 0 rgba(217,45,32,.55); }
  70%     { box-shadow: 0 0 0 12px rgba(217,45,32,0); }
}

.service__clock { display: flex; align-items: flex-end; gap: .5rem; }
.service__unit { display: flex; flex-direction: column; align-items: center; min-width: 3.1rem; }
.service__unit b {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.5rem, 3.2vw, 2.1rem); line-height: 1;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.service__unit span {
  font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-dim); margin-top: .35rem;
}
.service__sep {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.2rem, 2.6vw, 1.7rem);
  color: var(--accent); opacity: .5; padding-bottom: 1.1rem;
}
.service.is-live .service__clock { opacity: .35; }

@media (max-width: 780px) {
  .service__inner { justify-content: center; text-align: center; }
  .service__status { width: 100%; justify-content: center; }
  .service__clock  { width: 100%; justify-content: center; }
}

/* ---------- Year theme band ---------- */
.theme-year {
  position: relative; overflow: hidden;
  padding-block: clamp(2.8rem, 7vh, 5rem);
  background: var(--base-2);
  border-block: 1px solid var(--line);
  text-align: center;
}
.theme-year::before {
  content: ""; position: absolute; inset: 0;
  background: var(--glow-soft); pointer-events: none;
}
/* Words and portrait side by side once there is room. Below that the portrait
   drops underneath at a smaller size — a 333px source cannot carry a full
   column on a phone without going soft, and the words are the point. */
.theme-year__inner {
  position: relative; z-index: 1;
  display: grid; gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center; justify-items: center;
}
.theme-year__words { min-width: 0; }

.theme-year__pastor { margin: 0; text-align: center; }
.theme-year__pastor img {
  /* Capped at the source width — upscaling a cut-out shows every soft edge
     the mask left behind. */
  width: min(333px, 62vw); height: auto; display: block; margin-inline: auto;
  filter: drop-shadow(0 22px 46px rgba(0,0,0,.6));
}
.theme-year__pastor figcaption {
  font-family: var(--font-display); font-size: .84rem;
  letter-spacing: .06em; color: var(--text-muted); margin-top: .9rem;
}

@media (min-width: 900px) {
  .theme-year__inner {
    grid-template-columns: minmax(0, 1fr) auto;
    justify-items: start; text-align: left;
  }
  .theme-year__words { text-align: left; }
  .theme-year__pastor img { width: 300px; }
}
.theme-year__label {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--accent); margin-bottom: 1.4rem;
}
.theme-year__quote {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.9rem, 5.5vw, 4.2rem);
  line-height: 1.12; letter-spacing: var(--tracking-tight);
  max-width: 16ch; margin-inline: auto; color: var(--text);
}
.theme-year__quote em {
  font-style: normal;
  background: linear-gradient(100deg, var(--flame-amber), var(--flame-red));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* Sibling of the quote, so it needs its own width — the quote's 16ch would
   wrap this line badly. */
.theme-year__still {
  margin: .7rem auto 0;
  max-width: 24ch;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.2rem, 3vw, 2.1rem);
  line-height: 1.15;
  letter-spacing: var(--tracking-tight);
  color: var(--accent);
}
.theme-year__scripture {
  margin-top: 1.8rem; color: var(--text-muted);
  font-size: var(--fs-small); letter-spacing: .04em;
}

/* ---------- Signature "living light" (WebGL) ---------- */
.orb-section {
  position: relative; overflow: hidden;
  min-height: 92svh; display: grid; place-items: center;
  background: var(--base);
}
.orb {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
}
.orb canvas {
  width: 100%; height: 100%;
  display: block;
}
/* Fallback when WebGL is unavailable / reduced motion / mobile */
.orb--fallback canvas { display: none; }
.orb--fallback::before {
  content: "";
  position: absolute; inset: 0;
  background:
    radial-gradient(closest-side circle at 50% 50%, rgba(249,155,39,.55), transparent 60%),
    radial-gradient(closest-side circle at 50% 50%, rgba(240,72,40,.35),  transparent 78%);
  animation: orbBreathe 7s var(--ease-inout) infinite;
}
@keyframes orbBreathe {
  0%, 100% { transform: scale(.96); opacity: .82; }
  50%      { transform: scale(1.04); opacity: 1; }
}
.orb-section__content {
  position: relative; z-index: 2; text-align: center; max-width: 40ch;
}
.orb-section__content h2 { font-size: clamp(2rem, 5vw, 3.6rem); }

/* ---------- Verse band ---------- */
.verse {
  position: relative; text-align: center; overflow: hidden;
  padding-block: clamp(4rem, 10vh, 7rem);
  background: linear-gradient(165deg, var(--ember-deep) 0%, var(--purple-deep) 58%, var(--base) 100%);
}
.verse::after { content: ""; position: absolute; inset: 0; background: var(--glow-soft); }
.verse__inner { position: relative; z-index: 1; }
.verse p {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.35rem, 3vw, 2.35rem); line-height: 1.32;
  max-width: 20ch; margin-inline: auto; color: #fff;
}
.verse cite { display: block; margin-top: 1.2rem; font-style: normal;
  color: var(--flame-soft); letter-spacing: .05em; font-size: .95rem; }

/* ---------- Marquee ---------- */
.marquee { overflow: hidden; padding-block: 1.6rem;
  border-block: 1px solid var(--line); background: var(--base-2); }
/* NOTE: spacing lives on the items, not as flex `gap`.
   With `gap`, duplicating the track adds one extra gap between the two copies,
   so one copy's width no longer equals half the total — which is exactly what
   makes the loop jump. Per-item padding keeps every copy identical. */
.marquee__track { display: flex; width: max-content; }
.marquee__track span {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.1rem, 2.2vw, 1.7rem);
  color: var(--text-dim); display: inline-flex; align-items: center; gap: 3.5rem;
  white-space: nowrap;
  padding-right: 3.5rem;
}
.marquee__track span::after { content: "✦"; color: var(--accent); font-size: .8em; }

/* ---------- Footer ---------- */
.footer { background: var(--base-2); border-top: 1px solid var(--line);
  padding-block: 4rem 1.6rem; }
.footer__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1.2fr;
  gap: 2.5rem; margin-bottom: 3rem; }
.footer h4 { font-size: .78rem; letter-spacing: var(--tracking-wide);
  text-transform: uppercase; color: var(--accent); margin-bottom: 1.1rem; }
.footer__list { list-style: none; display: flex; flex-direction: column; gap: .6rem; font-size: .92rem; }
.footer__list a:not(.btn) { color: var(--text-muted); transition: color var(--dur-fast); }
.footer__list a:hover { color: var(--accent); }
.footer__bottom {
  border-top: 1px solid var(--line); padding-top: 1.4rem;
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  font-size: .8rem; color: var(--text-dim);
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1024px) {
  .cards { grid-template-columns: repeat(2, 1fr); }
  .footer__grid { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 900px) {
  .hamburger { display: block; }
  .header__cta .btn--ghost { display: none; }
  .menu {
    /* Sized explicitly rather than with inset:0, and that is not a style
       preference. Once the header is stuck it carries a backdrop-filter, and
       an element with a backdrop-filter becomes the containing block for its
       position:fixed descendants -- so inset:0 resolved against the header's
       85px box instead of the screen, cramming 505px of links into 144px. It
       read as the menu hiding behind the page, barely scrolling, and missing
       different items on different pages: one cause, three symptoms.
       The header sits at the top-left of the viewport regardless, so top and
       left of 0 still land where they should. 100vh first for old browsers,
       100dvh after it so phones discount the address bar. */
    position: fixed; top: 0; left: 0;
    width: 100vw; height: 100vh; height: 100dvh; z-index: -1;
    flex-direction: column; justify-content: flex-start; gap: .3rem;
    padding: 6rem var(--gutter) 3rem;
    background: var(--base); opacity: 0; visibility: hidden;
    transition: opacity var(--dur) var(--ease-out), visibility var(--dur);
    /* A panel taller than the screen once a submenu is open, so it scrolls on
       its own. `contain` stops the drag continuing into the page behind it
       once the list reaches its end -- otherwise the last flick scrolls the
       article underneath and the menu appears to jump. */
    overflow-y: auto; overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .menu.is-open { opacity: 1; visibility: visible; z-index: 1; }
  .menu > li { width: 100%; border-bottom: 1px solid var(--line); }
  .menu a, .menu__toggle { font-size: 1.05rem; padding-block: .95rem; width: 100%; }
  .menu__toggle { justify-content: space-between; }

  /* Mobile: submenus expand inline instead of floating */
  .submenu {
    position: static; opacity: 1; visibility: visible; transform: none;
    min-width: 0; background: none; border: 0; box-shadow: none;
    padding: 0 0 .6rem .8rem;
    display: none;
  }
  .submenu::before { display: none; }
  .has-sub.is-open .submenu { display: block; }
  .has-sub:hover .submenu { display: none; }
  .has-sub.is-open .submenu { display: block; }
  .submenu a { font-size: .95rem; padding-block: .55rem; }
  .give__grid { grid-template-columns: repeat(3, 1fr); }
  .stats { grid-template-columns: repeat(2, 1fr); gap: 2rem; }
  .topbar__contact { display: none; }
}

@media (max-width: 560px) {
  .cards, .footer__grid { grid-template-columns: 1fr; }
  .give__grid { grid-template-columns: repeat(2, 1fr); }
  .give { padding: 1.1rem; }
  .hero__meta { gap: 1.5rem; }
  .player__note { display: none; }
}

/* ============================================================
   PURPLE PASS

   The logo is flame on black and purple was always the secondary, but it had
   drifted almost entirely out of the build — ten uses across the whole
   stylesheet, most of them in one gradient. The page had gone monotone.

   Purple is used here as a second light source, never as text or a control
   colour. Flame stays the thing you act on; purple is the cool edge it is
   read against. That keeps the hierarchy intact — if both colours competed
   for attention, neither would mean anything.
   ============================================================ */

/* Alternating sections pick up a cool wash, so the page breathes between
   bands instead of running one flat tone top to bottom. */
.section--alt {
  background:
    radial-gradient(760px 420px at 82% 8%, rgba(106,71,184,.13), transparent 66%),
    radial-gradient(620px 380px at 8% 92%, rgba(75,46,131,.10), transparent 70%),
    var(--base-2);
}

/* Cards: purple at rest, flame on hover. The colour shift is what signals
   "this one is live" — more legible than a border alone. */
.card::before {
  background:
    radial-gradient(420px 260px at 50% 0%, rgba(106,71,184,.16), transparent 70%);
  opacity: 1;
}
.card:hover::before {
  background:
    radial-gradient(420px 260px at 50% 0%, rgba(249,155,39,.20), transparent 70%);
}

/* Page headers get the same counterpoint the home hero already had.
   :not() is load-bearing. This rule and .page-hero--banner::before have equal
   specificity, and this one comes later in the file — so without the guard it
   replaced the banner scrim entirely on every page that has a photographic
   header. The Give heading disappeared into its own background because of it. */
.page-hero:not(.page-hero--banner)::before {
  background:
    radial-gradient(680px 420px at 88% 10%, rgba(106,71,184,.24), transparent 64%),
    var(--glow-hero);
}

/* Banner headers get their purple mixed INTO the scrim rather than instead
   of it, so the photograph still has something to sit behind the type. */
.page-hero--banner::before {
  background:
    radial-gradient(700px 460px at 84% 12%, rgba(106,71,184,.30), transparent 62%),
    linear-gradient(90deg,
      rgba(10,7,5,.94) 0%, rgba(10,7,5,.86) 32%,
      rgba(10,7,5,.58) 64%, rgba(10,7,5,.42) 100%),
    linear-gradient(to bottom,
      rgba(10,7,5,.72) 0%, rgba(10,7,5,.20) 42%, rgba(10,7,5,.78) 100%);
}

/* Panels and the giving grid: a purple hairline along the top edge, echoing
   the one already on the dropdown menus. */
.panel-tint, .give__grid { position: relative; }
.give__grid::before {
  content: ""; position: absolute; top: -1px; left: 12%; right: 12%; height: 2px;
  background: linear-gradient(90deg, transparent, var(--purple-glow), var(--accent), transparent);
  border-radius: 2px; opacity: .7;
}

/* Footer sits deepest — the cool end of the page. */
.footer {
  background:
    radial-gradient(900px 500px at 15% 0%, rgba(75,46,131,.16), transparent 68%),
    var(--base-2);
}

/* The theme band already carried the year's words; giving it the strongest
   purple makes it read as the page's one still moment. */
.theme-year {
  background:
    radial-gradient(700px 460px at 20% 20%, rgba(106,71,184,.18), transparent 66%),
    radial-gradient(620px 400px at 84% 84%, rgba(240,72,40,.10), transparent 70%),
    var(--base-2);
}

/* ---------- More purple ----------
   Pushed further per the brief. Still never text and never a control — the
   flame has to stay the thing you act on. These are all atmosphere: edges,
   glows and grounds. */

/* Buttons pick up a purple rim on hover, so the ghost variant stops reading
   as a plain outline. */
.btn--ghost:hover { --btn-bd: var(--purple-glow); --btn-fg: #C9B3FF; }

/* Section headings get a purple-to-flame rule beneath them. */
.section__title::after {
  content: ""; display: block; width: 62px; height: 2px; margin: .9rem auto 0;
  background: linear-gradient(90deg, var(--purple-glow), var(--accent));
  border-radius: 2px;
}

/* Panels and cards sit on a cooler ground. */
.panel, .give__btn, .evcard, .album, .legacy__item {
  background:
    linear-gradient(160deg, rgba(75,46,131,.10), transparent 55%),
    var(--surface);
}

/* Album and event tiles glow purple on hover before the flame border lands. */
.album:hover, .evcard:hover {
  box-shadow: 0 18px 48px rgba(75,46,131,.30);
}

/* The broadcast panel — the most-looked-at block on the home page. */
.player { box-shadow: 0 24px 70px rgba(75,46,131,.24); }

/* Hairline dividers across the site pick up the two-colour gradient that was
   previously only on the dropdown menus. */
hr, .prose hr {
  border: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--purple-glow), var(--accent), transparent);
  opacity: .5;
}

/* Blockquotes in prose get a purple spine. */
.prose blockquote {
  border-left: 3px solid var(--purple-glow);
  padding-left: 1.2rem; margin-left: 0;
}

/* ---------- Scan-to-give codes ----------
   On a white tile, always. A QR code is read by thresholding light against
   dark, and inverting or tinting one is the quickest way to make it
   unscannable on a phone that is already struggling in low light. */
.qr { margin-top: 3.6rem; text-align: center; }
.qr__grid {
  display: flex; justify-content: center; gap: clamp(1rem, 4vw, 2.4rem);
  flex-wrap: wrap; margin-top: 1.8rem;
}
.qr__card {
  margin: 0; background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: 1rem 1rem 1.1rem; width: min(220px, 42vw);
}
.qr__card img {
  width: 100%; height: auto; display: block;
  background: #fff; border-radius: 8px; padding: 8px;
}
.qr__card figcaption { margin-top: .8rem; }
.qr__card strong {
  display: block; font-family: var(--font-display); font-size: 1rem; color: var(--text);
}
.qr__card small { color: var(--text-muted); font-size: .82rem; word-break: break-all; }
.qr__note {
  max-width: 46ch; margin: 1.6rem auto 0;
  font-size: .86rem; color: var(--text-muted); line-height: 1.6;
}

/* ---------- Back to top ----------
   Sits to the right of the floating social cluster and shares its show/hide,
   so the two appear and leave together rather than as two separate events.
   Bottom-right rather than centre: it is a utility, and the social links are
   the thing worth putting in front of someone. */
.to-top {
  position: fixed; right: clamp(1rem, 3vw, 1.8rem);
  bottom: clamp(.9rem, 3vh, 1.6rem); z-index: 55;
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center;
  background: rgba(22,16,12,.82); backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--line-strong); color: var(--text-muted);
  box-shadow: 0 12px 34px rgba(0,0,0,.5);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(14px);
  transition: opacity .4s var(--ease-out), transform .4s var(--ease-out),
              visibility .4s, color var(--dur-fast), border-color var(--dur-fast);
}
.to-top.is-shown {
  opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0);
}
.to-top:hover { color: var(--accent); border-color: var(--accent); }
.to-top svg { width: 19px; height: 19px; }

/* ============================================================
   FORMS — prayer, contact
   ============================================================ */

/* The honeypot. Moved off screen rather than display:none — the better bots
   check for that and skip the field, which is exactly what we do not want
   them to do. */
.trap {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden;
}

.formwrap { max-width: 62ch; margin: 3rem auto 0; }
.fld { display: block; margin-bottom: 1.1rem; }
.fld > input, .fld > textarea, .fld > select {
  display: block; width: 100%; margin-top: .45rem;
  padding: .85rem 1rem; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--line-strong);
  color: var(--text); font: 400 1rem/1.5 var(--font-body);
}
.fld > input:focus, .fld > textarea:focus {
  outline: 2px solid rgba(249,155,39,.55); outline-offset: 2px;
  border-color: var(--accent);
}
.fld > textarea { resize: vertical; min-height: 8rem; }
.fld { font-family: var(--font-display); font-size: .88rem; color: var(--text-muted); }
.req { color: var(--accent); }
.fld__note { font-size: .85rem; color: var(--text-dim); margin: -.4rem 0 1.2rem; line-height: 1.6; }

.fld--check {
  display: flex; gap: .7rem; align-items: flex-start;
  font-family: var(--font-body); font-size: .92rem;
  color: var(--text-muted); line-height: 1.6; margin-bottom: 1.6rem;
}
.fld--check input { margin-top: .25rem; flex: none; width: 18px; height: 18px; }

.field-pair { display: grid; gap: 0 1rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 620px) { .field-pair { grid-template-columns: 1fr; } }

.formnote {
  padding: .8rem 1rem; border-radius: var(--radius-sm); margin-bottom: 1rem;
  font-size: .92rem; border: 1px solid;
}
.formnote--error {
  background: rgba(240,72,40,.10); border-color: rgba(240,72,40,.42); color: #ff9c85;
}

/* The state after sending. Given real weight rather than a one-line message —
   someone who has just written down something difficult should get more back
   than a grey toast. */
.formdone {
  max-width: 52ch; margin-inline: auto; text-align: center;
  padding: clamp(2.5rem, 7vw, 4rem) 1.5rem;
  background:
    radial-gradient(600px 320px at 50% 0%, rgba(106,71,184,.16), transparent 70%),
    var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.formdone__mark {
  width: 62px; height: 62px; margin: 0 auto 1.3rem; border-radius: 50%;
  display: grid; place-items: center; font-size: 1.7rem;
  background: rgba(61,163,93,.16); color: #7fd89b; border: 1px solid rgba(61,163,93,.4);
}
.formdone h2 { font-family: var(--font-display); font-size: 1.7rem; margin-bottom: .8rem; }
.formdone p { color: var(--text-muted); margin-bottom: 1rem; }
.formdone .btn { margin-top: 1rem; }

/* ---------- Contact layout ---------- */
.contact { display: grid; gap: clamp(1.6rem, 4vw, 3rem); grid-template-columns: 1.35fr 1fr; }
@media (max-width: 860px) { .contact { grid-template-columns: 1fr; } }
.contact__form { min-width: 0; }
.contact__side { display: flex; flex-direction: column; gap: 1rem; }
.contact__card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.4rem 1.5rem;
}
.contact__card h3 {
  font-family: var(--font-display); font-size: 1.05rem; margin-bottom: .6rem;
}
.contact__card p { color: var(--text-muted); font-size: .95rem; line-height: 1.65; margin-bottom: .9rem; }
.contact__phone {
  font-family: var(--font-display); font-size: 1.25rem; color: var(--accent);
}

/* ---------- Long forms (scholarship) ---------- */
.formwrap--wide { max-width: 74ch; }
.fset {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.6rem 1.5rem 1.2rem; margin-bottom: 1.6rem;
  background: linear-gradient(160deg, rgba(75,46,131,.08), transparent 60%), var(--surface);
}
.fset > legend {
  font-family: var(--font-display); font-size: 1.05rem; color: var(--accent);
  padding: 0 .6rem; margin-left: -.6rem;
}
.fset__sub {
  font-family: var(--font-display); font-size: .95rem; color: var(--text);
  margin: 1.4rem 0 .8rem;
}
.fld__hint { display: block; margin-top: .3rem; font-size: .8rem; color: var(--text-dim); }
.radios-inline { border: 0; padding: 0; margin: 0 0 1.2rem; }
.radios-inline > legend {
  font-family: var(--font-display); font-size: .88rem;
  color: var(--text-muted); padding: 0; margin-bottom: .5rem;
}
.radios-inline label {
  display: inline-flex; align-items: center; gap: .4rem;
  margin-right: 1.2rem; font-size: .92rem; color: var(--text-muted); cursor: pointer;
}
.fld > input[type=file] {
  padding: .6rem; font-size: .88rem; background: var(--base);
}

/* ---------- HLCB current quarter ----------
   The flyer is purple and gold; these panels lean on the site's purple so the
   page reads as continuous with the printed piece the church is already
   handing out, without copying it. */
.quarter { text-align: center; max-width: 60ch; margin-inline: auto; }
.quarter__kicker {
  font-family: var(--font-display); font-size: var(--fs-micro);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--accent); margin-bottom: 1rem;
}
.quarter__title {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.6rem, 4vw, 2.6rem); line-height: 1.15; text-wrap: balance;
}
.quarter__classes {
  display: flex; flex-direction: column; gap: .7rem;
  margin: 2.2rem auto 0; max-width: 34ch; text-align: left;
}
.quarter__class {
  display: grid; grid-template-columns: auto 1fr; gap: .2rem .8rem;
  align-items: center;
  padding: 1rem 1.2rem; border-radius: var(--radius);
  background: linear-gradient(135deg, rgba(75,46,131,.24), rgba(22,16,12,.5));
  border: 1px solid var(--line-strong);
}
.quarter__class strong { font-family: var(--font-display); font-size: 1.08rem; }
.quarter__class small {
  grid-column: 2; font-size: .78rem; color: var(--text-muted); font-style: italic;
}
.quarter__tick {
  grid-row: 1 / span 2; width: 26px; height: 26px; border-radius: 6px;
  display: grid; place-items: center; font-size: .85rem;
  background: var(--accent); color: var(--btn-gold-text);
}

.quarter__reg {
  display: grid; gap: 1.2rem; margin-top: 2.6rem;
  grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
  max-width: 64ch; margin-inline: auto;
}
.regcard {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.5rem 1.6rem; text-align: center;
}
.regcard h3 { font-family: var(--font-display); font-size: 1.1rem; margin-bottom: .3rem; }
.regcard__time { color: var(--accent); font-size: .9rem; margin-bottom: 1rem; }
.regcard__dates { list-style: none; padding: 0; margin: 0 0 1.2rem; }
.regcard__dates li {
  padding: .45rem 0; border-bottom: 1px solid var(--line);
  font-size: .95rem; color: var(--text);
}
.regcard__dates li:last-child { border-bottom: 0; }
.regcard__where { font-size: .9rem; color: var(--text-muted); margin-bottom: 1rem; }

/* A required disclosure, not a footnote we chose to add — so it is legible
   rather than tucked away at 10px. */
.disclosure {
  margin-top: 2.6rem; padding-top: 1.4rem; border-top: 1px solid var(--line);
  font-size: .82rem; color: var(--text-dim); line-height: 1.6; max-width: 56ch;
  margin-inline: auto;
}

/* ---------- Blog ---------- */
.postlist { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.6rem;
            grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
.postcard {
  display: flex; flex-direction: column; height: 100%;
  background: linear-gradient(160deg, rgba(75,46,131,.10), transparent 55%), var(--surface);
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
}
.postcard:hover { border-color: var(--accent); }
.postcard__img { display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--surface-2); }
.postcard__img img { width: 100%; height: 100%; object-fit: cover; display: block;
                     transition: transform .7s var(--ease-out); }
.postcard:hover .postcard__img img { transform: scale(1.04); }
.postcard__meta { display: block; padding: 1.2rem 1.35rem 1.4rem; }
.postcard__scripture {
  display: block; font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); margin-bottom: .4rem;
}
.postcard__meta strong {
  font-family: var(--font-display); font-size: 1.2rem; display: block;
  margin-bottom: .5rem; text-wrap: balance;
}
.postcard__excerpt { display: block; color: var(--text-muted); font-size: .93rem;
                     line-height: 1.6; margin-bottom: .8rem; }
.postcard__meta small { color: var(--text-dim); font-size: .82rem; }

.postimg { border-radius: 16px; overflow: hidden; border: 1px solid var(--line);
           margin-bottom: 2.6rem; max-width: 900px; margin-inline: auto; }
.postimg img { width: 100%; height: auto; display: block; }
/* Wider measure than .prose default — a blog post is read straight through,
   and 68ch of body text is the comfortable ceiling. */
.postbody { max-width: 68ch; margin-inline: auto; font-size: 1.06rem; }
.postbody p { margin-bottom: 1.3rem; line-height: 1.8; }

.postvideo { max-width: 900px; margin: 3.5rem auto 0; text-align: center; }
.postvideo__frame {
  position: relative; aspect-ratio: 16 / 9; margin-top: 1.5rem;
  border-radius: 14px; overflow: hidden; border: 1px solid var(--line); background: #000;
}
.postvideo__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ---------------------------------------------------------------- tribute --
   The First Lady's header. The three portraits arrive as one transparent PNG
   so the arrangement stays whatever was decided in Photoshop; everything else
   here — gradient, name, dates — is live so it can be corrected without
   reopening the artwork. */
.tribute {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; align-items: flex-end;
  min-height: clamp(360px, 44vw, 580px);
  border-bottom: 1px solid rgba(200, 162, 75, .32);
  background:
    radial-gradient(115% 88% at 74% 106%, rgba(255, 214, 140, .18) 0%, rgba(255, 214, 140, 0) 56%),
    linear-gradient(103deg, #150B24 0%, #2E1852 26%, #4B2E83 48%, #6B2059 76%, #8C1F3E 100%);
}
/* A cross, barely there. The only ornament — enough to say what this is
   without turning the header into a poster. */
.tribute__cross {
  position: absolute; inset: 0; z-index: 0; opacity: .055; transform: translateX(12%);
  background:
    linear-gradient(to right, transparent calc(50% - 9px), #fff calc(50% - 9px),
                    #fff calc(50% + 9px), transparent calc(50% + 9px)) no-repeat 0 0 / 100% 100%,
    linear-gradient(to bottom, transparent calc(27% - 9px), #fff calc(27% - 9px),
                    #fff calc(27% + 9px), transparent calc(27% + 9px)) no-repeat 0 0 / 100% 100%;
  -webkit-mask-image: radial-gradient(58% 68% at 50% 40%, #000 0%, transparent 72%);
          mask-image: radial-gradient(58% 68% at 50% 40%, #000 0%, transparent 72%);
}
/* Darkens the left so the name always has something to sit on, whatever the
   gradient is doing behind it. */
.tribute__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(80% 118% at 14% 52%, rgba(11, 7, 16, .60) 0%, rgba(11, 7, 16, 0) 62%);
}
.tribute__inner {
  position: relative; z-index: 3; width: min(1240px, 92vw); margin: 0 auto;
  padding: clamp(2rem, 4.6vw, 3.2rem) 0 0;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.16fr);
  align-items: end; gap: clamp(.5rem, 2vw, 1.5rem);
}
.tribute__type { padding-bottom: clamp(1.6rem, 4vw, 3rem); }

.tribute__eyebrow {
  margin: 0 0 1.05rem; font-size: .74rem; font-weight: 600;
  letter-spacing: .24em; text-transform: uppercase; color: var(--gold-light, #E4C478);
}
.tribute__eyebrow::after {
  content: ""; display: block; width: 54px; height: 2px; margin-top: .7rem;
  background: linear-gradient(90deg, #C8A24B, rgba(200, 162, 75, 0));
}
.tribute__name { margin: 0; font-family: var(--font-display, Cinzel), serif; line-height: .95; color: #fff; }
.tribute__pre {
  display: block; font-weight: 600; letter-spacing: .38em; margin-bottom: .5rem;
  font-size: clamp(.7rem, 1.3vw, .95rem); text-transform: uppercase; color: #CDBBE8;
}
.tribute__given {
  display: block; font-weight: 400; font-size: clamp(1.8rem, 4.4vw, 3.6rem);
  text-shadow: 0 2px 26px rgba(0, 0, 0, .45);
}
.tribute__sur {
  display: block; font-weight: 900; margin-top: .05em;
  font-size: clamp(2.4rem, 6.6vw, 5.6rem); letter-spacing: .012em;
  text-transform: uppercase;
  background: linear-gradient(178deg, #F6E3AE 4%, #E4C478 26%, #C8A24B 54%, #8C6B26 82%, #E9CE92 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 3px 18px rgba(0, 0, 0, .5));
}
.tribute__meta {
  margin: 1.1rem 0 0; font-size: clamp(.72rem, 1.2vw, .9rem); font-weight: 500;
  letter-spacing: .16em; line-height: 1.75; text-transform: uppercase;
  color: rgba(255, 255, 255, .86);
}
/* Two deliberate lines. Left to wrap on its own it broke mid-phrase — and this
   is where her dates will go, which makes the line longer still. */
.tribute__meta span { display: block; }
.tribute__meta span + span { color: rgba(255, 255, 255, .62); }

/* Bleeds off the right edge so the group reads as standing in the banner
   rather than pasted into a box. */
.tribute__figures { display: block; align-self: end; margin-right: -1.5%; }
.tribute__figures img {
  display: block; width: 100%; height: auto;
  filter: drop-shadow(-6px 14px 40px rgba(0, 0, 0, .55));
  /* The artwork is cropped hard at its left edge -- a fifth of the image
     height is solid pixels running off the side, which reads as a severed
     arm. Fading that edge into the gradient turns a cut into a soft entry.
     Replace the artwork with an uncropped export and this line can go. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 8%);
          mask-image: linear-gradient(to right, transparent 0, #000 8%);
}

@media (max-width: 820px) {
  /* Plain stacking, not a grid.

     The grid was bottom-aligning its rows inside a flex container, and on a
     narrow screen the type block came out taller than the row it was given:
     it rode up 31px over the photograph, and its last line fell 11px past the
     section and was clipped away by overflow:hidden. Neither is a thing you
     can tune with padding -- the row height was the problem, so the row goes. */
  .tribute { display: block; min-height: 0; }
  /* Flex column, not block: the photograph has to come first, and order only
     means anything in a flex or grid context. */
  .tribute__inner {
    display: flex; flex-direction: column; width: auto; margin: 0; gap: 0;
    /* Roomier than it looks like it needs to be, on purpose. The reveal
       animation holds each element 34px below its resting place while it fades
       in, and this section clips what hangs over its edge -- so with only the
       settled layout's 25px of slack, the last line of the strapline was being
       sliced off for the whole of every reveal, and permanently if the reveal
       never ran. Desktop hid the fault behind 580px of spare height. */
    padding: 0 0 clamp(4rem, 12vw, 5rem);
  }
  .tribute__figures { order: -1; margin: 0 0 .9rem; }
  .tribute__type { padding: 0 4vw; }
  .tribute__scrim {
    background: linear-gradient(180deg, rgba(11, 7, 16, 0) 34%, rgba(11, 7, 16, .5) 82%);
  }
}
