/* ===================================================================
   North Point Church
   Brand palette: black · off-black · white · off-white · red #B23035
   Font: Montserrat standing in for Proxima Nova
   =================================================================== */

:root {
  --black: #121213;
  --offblack: #1e1e20;
  --white: #ffffff;
  --offwhite: #f4f2ee;
  --red: #b23035;
  --red-ink: #8f2429;

  /* legacy names remapped to brand tokens */
  --navy: #1e1e20;        /* off-black — dark surfaces + headings */
  --slate: #57575a;       /* muted body text */
  --cream: #f4f2ee;       /* off-white page background */
  --beige: #ece9e3;       /* light neutral (kept for app buttons) */
  --beige-ink: #dcd8d0;
  --muted: #97979a;
  --royal: #b23035;       /* primary red accent */
  --royal-ink: #8f2429;
  --ink: #121213;         /* near-black body text */
  --line: rgba(18, 18, 19, 0.14);
  --line-light: rgba(255, 255, 255, 0.16);

  --font: "Montserrat", Arial, sans-serif;

  --max: 92rem;
  --gutter: clamp(1.25rem, 4vw, 3.5rem);
  --section-y: clamp(3.5rem, 8vw, 6.5rem);
  --radius: 6px;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font);
  font-weight: 400;
  color: var(--ink);
  background: var(--cream);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

.container { width: 100%; max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -999px; top: 0; z-index: 100;
  background: var(--navy); color: var(--cream); padding: 0.75rem 1.25rem; text-decoration: none;
}
.skip-link:focus { left: 0.5rem; top: 0.5rem; }
:focus-visible { outline: 3px solid var(--navy); outline-offset: 2px; }

/* Image blocks — temporary free-license stock, swap for North Point photos */
.ph {
  position: relative;
  background-color: #d8d5d0;
  background-image: linear-gradient(135deg, #e0ddd8, #cbc8c2);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  overflow: hidden;
}

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.9rem 1.75rem;
  font-family: var(--font);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1.5px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}
.btn--sm { padding: 0.55rem 1.1rem; font-size: 0.68rem; }
.btn--block { width: 100%; }

.btn--beige { background: var(--beige); color: var(--navy); border-color: var(--beige); }
.btn--beige:hover { background: var(--beige-ink); border-color: var(--beige-ink); }

.btn--dark { background: var(--navy); color: var(--cream); border-color: var(--navy); }
.btn--dark:hover { background: var(--black); border-color: var(--black); }

.btn--outline-light { background: transparent; color: var(--cream); border-color: var(--line-light); }
.btn--outline-light:hover { background: var(--cream); color: var(--navy); border-color: var(--cream); }

.btn--outline-dark { background: transparent; color: var(--navy); border-color: var(--navy); }
.btn--outline-dark:hover { background: var(--navy); color: var(--cream); border-color: var(--navy); }

.btn--royal { background: var(--royal); color: #fff; border-color: var(--royal); }
.btn--royal:hover { background: var(--royal-ink); border-color: var(--royal-ink); }

.btn--white { background: #fff; color: #000; border-color: #fff; }
.btn--white:hover { background: #f2f1ec; border-color: #f2f1ec; }

.btn--red { background: #B23035; color: #fff; border-color: #B23035; }
.btn--red:hover { background: #8f2429; border-color: #8f2429; }

.btn--royal-outline { background: transparent; color: var(--royal); border-color: var(--royal); }
.btn--royal-outline:hover { background: var(--royal); color: #fff; }

/* Icon inside a button. .btn is already inline-flex with a gap, so the glyph
   only needs sizing — em-based so it tracks the label rather than needing a
   second rule per button size. */
.btn > svg { width: 1.15em; height: 1.15em; flex: none; }

.btn--white-pill { background: #fff; color: var(--navy); border-color: #fff; }
.btn--white-pill span { font-weight: 800; margin-left: 0.15em; }
.btn--white-pill:hover { background: var(--cream); border-color: var(--cream); }

/* ============================================================
   HEADER
   ============================================================ */
.site-header { background: var(--cream); position: relative; z-index: 40; }

.utility { border-bottom: 1px solid var(--line); }
.utility-inner {
  display: flex; align-items: center; justify-content: space-between;
  min-height: 84px; gap: 1rem; padding-block: 0.6rem;
}
.utility-logo { display: inline-flex; align-items: center; }
.utility-logo img { height: 52px; width: auto; }
.utility-actions { display: flex; align-items: center; gap: 0.75rem; }

.menu-btn {
  display: inline-flex; align-items: center; gap: 0.6rem;
  background: none; border: 0; cursor: pointer;
  font-family: var(--font); font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--navy);
  padding: 0.5rem 0.25rem;
}
/* Hamburger that morphs into an X.
   Geometry: three 2px bars with 3px gaps = 12px tall, so the bars sit at
   y=1, y=6 and y=11. Moving the outer two 5px toward the middle puts all
   three on the same centre line, where rotating them 45deg either way makes
   the X. The middle bar scales out from the centre as they travel.

   Driven entirely off aria-expanded, which main.js already toggles on the
   button — no extra class and no JS change, so the icon state can never
   disagree with what screen readers are told. */
.menu-lines { display: inline-flex; flex-direction: column; gap: 3px; width: 20px; }
.menu-lines span {
  height: 2px; background: var(--navy); border-radius: 2px;
  transform-origin: center;
  transition: transform 0.26s cubic-bezier(0.2, 0.7, 0.3, 1),
              opacity 0.18s ease;
}
.menu-btn[aria-expanded="true"] .menu-lines span:nth-child(1) {
  transform: translateY(5px) rotate(45deg);
}
.menu-btn[aria-expanded="true"] .menu-lines span:nth-child(2) {
  opacity: 0; transform: scaleX(0);
}
.menu-btn[aria-expanded="true"] .menu-lines span:nth-child(3) {
  transform: translateY(-5px) rotate(-45deg);
}

@media (prefers-reduced-motion: reduce) {
  .menu-lines span { transition: none; }
}

/* Desktop primary nav. Previously every link was hidden behind the menu
   button at all widths, so a first-time guest couldn't see that Plan Your
   Visit existed without clicking to find out. */
.primary-nav { display: none; align-items: center; gap: clamp(1.1rem, 2.2vw, 2rem); }
.primary-nav-link {
  position: relative;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; text-decoration: none; color: var(--navy);
  padding-block: 0.4rem;
  white-space: nowrap;
}
.primary-nav-link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--red);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.18s ease;
}
.primary-nav-link:hover::after,
.primary-nav-link.is-current::after { transform: scaleX(1); }
.primary-nav-link.is-current { color: var(--red); }

/* --- Dropdowns -------------------------------------------------------
   Locations, Connect and Ministries each open a panel. The trigger is an
   <a> when the parent is a real page and a <button> when it is only a
   grouping label, so both need the same look — hence the reset below.

   Opening is CSS-first: :hover and :focus-within cover pointer and keyboard
   without a line of script. The JS in main.js only adds click-to-open for
   touch, where there is no hover, and keeps aria-expanded honest. */
.nav-item { position: relative; display: inline-flex; }
.has-dropdown > .primary-nav-link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  /* Full reset so the <button> triggers (Connect, Ministries) sit on the
     same baseline and left edge as the <a> ones. */
  background: none; border: 0; cursor: pointer;
  font-family: var(--font); font-size: 0.72rem; line-height: inherit;
  padding-inline: 0;
}
.nav-caret {
  width: 0; height: 0;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
  transition: transform 0.18s ease;
}
.has-dropdown:hover .nav-caret,
.has-dropdown:focus-within .nav-caret,
.has-dropdown[data-open="true"] .nav-caret { transform: rotate(180deg); }

.nav-dropdown {
  position: absolute; top: 100%; left: 50%;
  transform: translate(-50%, 6px);
  min-width: 12.5rem;
  display: flex; flex-direction: column;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.4rem;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.12);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.16s ease, transform 0.16s ease;
  z-index: 50;
}
/* A gap between the trigger and the panel would drop the hover on the way
   down, so the panel starts flush and pads itself away from the bar. */
.nav-dropdown::before {
  content: ""; position: absolute; left: 0; right: 0; top: -10px; height: 10px;
}
.has-dropdown:hover .nav-dropdown,
.has-dropdown:focus-within .nav-dropdown,
.has-dropdown[data-open="true"] .nav-dropdown {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: translate(-50%, 10px);
}
.nav-dropdown-link {
  display: block;
  padding: 0.6rem 0.75rem;
  border-radius: 10px;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; text-decoration: none; color: var(--navy);
  white-space: nowrap;
}
.nav-dropdown-link:hover { background: var(--line-light); }
.nav-dropdown-link.is-current { color: var(--red); }

@media (prefers-reduced-motion: reduce) {
  .nav-dropdown, .nav-caret { transition: none; }
}

@media (min-width: 68rem) {
  .primary-nav { display: flex; }
  .menu-btn { display: none; }
}
@media (max-width: 46rem) {
  .nav-cta { display: none; }   /* Plan Your Visit stays in the menu panel */
}

/* Mobile menu panel */
.menu-panel-link.is-current { color: var(--red); }
/* Separates the primary nav from the footer-level pages below it. flex-basis
   is explicitly auto: it was 100%, which meant "full width" while the panel
   was a wrapping row but would mean "full height" now that it's a column. */
.menu-panel-rule {
  flex: 0 0 auto;
  width: 100%; height: 1px;
  background: var(--line-light);
  margin-block: 0.6rem;
}

.brandbar { display: flex; justify-content: center; padding-block: clamp(1.75rem, 4vw, 2.75rem) 1rem; }
.wordmark {
  font-size: clamp(1.75rem, 5vw, 2.9rem);
  font-weight: 300;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--navy);
}
.wordmark strong { font-weight: 800; }


/* Menu panel */
.menu-panel {
  display: none;
  background: var(--navy);
  border-top: 1px solid var(--line-light);
}
.menu-panel[data-open="true"] { display: block; }
/* One item per row, stacked down the viewport. This was a wrapping
   horizontal row, which reflowed into ragged lines of two and three links at
   phone widths and made the list hard to scan and easy to mis-tap. The panel
   is only reachable below 68rem, where the menu button lives, so a column is
   the only layout it ever needs. */
.menu-panel-inner {
  display: flex; flex-direction: column; align-items: stretch;
  gap: 0;
  padding-block: 0.75rem;
}
/* Keyed off the CLASS, not the element: Connect and Ministries are <button>
   in this panel, not <a>, and an element-only selector left them in the
   browser's default button styling beside a column of uppercase links. */
.menu-panel a,
.menu-panel .menu-panel-link {
  color: var(--cream); text-decoration: none;
  font-size: 0.8rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
}
/* Full-row links with a ~44px tap target, so the hit area is the whole row
   rather than just the width of the words. */
.menu-panel-inner > a {
  display: block;
  padding-block: 0.85rem;
}
.menu-panel a:hover,
.menu-panel .menu-panel-link:hover { color: var(--beige); }

/* --- Collapsible groups in the mobile panel --------------------------
   Closed by default; main.js flips [hidden] and aria-expanded together.
   A parent that is a page keeps its link and sits in a row beside its own
   caret button, so the name navigates and only the caret toggles. A parent
   that is only a label is the button outright. */
.menu-panel-row { display: flex; align-items: center; }
/* Restates what .menu-panel-inner > a gave it before the group div came
   between them. */
.menu-panel-row > .menu-panel-link {
  flex: 1; display: block; padding-block: 0.85rem;
}

/* The caret's tap target is the full row height, not just the glyph. */
.menu-panel-toggle {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 3rem; align-self: stretch;
  background: none; border: 0; cursor: pointer;
  color: var(--cream);
}
/* The label parents (Connect, Ministries) reuse the link's look. */
.menu-panel-link--btn {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%;
  background: none; border: 0; cursor: pointer;
  font-family: var(--font); text-align: left;
  padding-block: 0.85rem;
  padding-inline: 0;
}
.menu-panel-link--btn .menu-panel-caret { margin-right: 1.25rem; }

.menu-panel-caret {
  width: 0; height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  transition: transform 0.2s ease;
}
[aria-expanded="true"] .menu-panel-caret { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
  .menu-panel-caret { transition: none; }
}

.menu-panel-sub > .menu-panel-link--sub {
  display: block;
  padding-left: 1rem;
  padding-block: 0.7rem;
  font-size: 0.75rem;
  font-weight: 500;
  opacity: 0.85;
}

/* ============================================================
   SHARED SECTION + HEADINGS (mixed-weight, one.church style)
   ============================================================ */
.section { padding-block: var(--section-y); }
.section--cream { background: var(--cream); }

/* On-dark text. .head-sub (--slate) fails contrast on a navy band, so it is
   scoped here once rather than per page. */
.section--navy .head-sub { color: rgba(255, 255, 255, 0.78); }
.section--navy .head { color: var(--cream); }
.section--band { background: #eae7e1; }   /* neutral band — same value .watch and .events use */
.section--navy { background: var(--navy); color: var(--cream); }

.head {
  font-size: clamp(1.8rem, 4vw, 2.9rem);
  font-weight: 300;
  line-height: 1.08;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--navy);
  margin: 0;
}
.head strong { font-weight: 800; }
.head--light { color: var(--cream); }
.head--xl { font-size: clamp(2rem, 5.2vw, 4rem); }

.head-block { margin-bottom: clamp(2rem, 5vw, 3.25rem); }
.head-block--center { text-align: center; max-width: 40rem; margin-inline: auto; }

/* The 40rem measure above is sized for .head-sub body copy, which makes a
   long .head--xl headline wrap mid-phrase. --wide widens the block for the
   headline's sake and hands the original 40rem back to the body copy, so the
   paragraph measure never changes. text-wrap: balance keeps the break
   sensible once the headline does wrap on narrow screens.
   Must follow --center to win the cascade. */
.head-block--wide { max-width: 52rem; }
.head-block--wide .head { text-wrap: balance; }
.head-block--wide .head-sub { max-width: 40rem; margin-inline: auto; }
.head-block--row {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
}
.head-sub { margin: 1rem 0 0; color: var(--slate); font-size: 0.98rem; }

/* ---- Hero headlines hold one line --------------------------------------
   Every hero <h1> used to inherit the head-block's 40rem measure, which is
   sized for body copy — so "See for yourself." broke across two lines with
   most of the container sitting empty either side.

   The fix sizes the headline to its container instead of wrapping inside it:
   the line is held with `nowrap` and the font shrinks only as far as it has
   to, capped at the usual 4rem so short headlines don't balloon.

   `--h1-chars` is written onto each h1 at build time by the
   heroHeadlineLength transform in eleventy.config.js — nothing to maintain
   per page. 0.74 is the average advance of one uppercase Montserrat
   character at this letter-spacing (measured ~0.71) plus a little margin, so
   a headline of unusually wide letters still can't spill.

   Only above 60rem. Narrower than that there isn't room to shrink a long
   headline to one line and still have it readable, so the mobile rule below
   is left alone and long headlines wrap as before. */
@media (min-width: 60rem) {
  /* --overlay is the homepage's video hero, which opts out entirely — no
     containment where nothing measures against it. */
  .hero:not(.hero--overlay) > .container { container-type: inline-size; }

  /* .hero-stage-title is excluded by name: the homepage headline sits over
     video inside a 46rem box and is meant to break across two lines (Bryan,
     2026-09-10). Everything else here is a flat cream hero where the single
     line is the point. */
  h1.head--xl:not(.hero-stage-title) {
    white-space: nowrap;
    font-size: min(4rem, calc(var(--fit-width) / (var(--h1-chars, 26) * 0.74)));
  }

  /* The head-block's measure exists for the paragraph under the headline.
     Hand the headline the full container and give the measure back to
     everything else in the block, so body copy reads exactly as it did —
     hence --measure carrying each variant's original max-width rather than a
     flat number, which would have re-broken the lines on the --wide pages. */
  .hero .head-block--center { --measure: 40rem; max-width: none; }
  .hero .head-block--wide { --measure: 52rem; }
  .hero .head-block--center > :not(h1) {
    max-width: var(--measure); margin-inline: auto;
  }
  .hero:not(.hero--overlay) > .container h1.head--xl { --fit-width: 100cqw; }
}

/* .eyebrow / .eyebrow--center removed 2026-09-10 — every page's eyebrows were
   stripped. The handful carrying real information were converted rather than
   deleted: the campus breadcrumb (.crumb), the campus pastor's title
   (.about-pastor-title), the event category badge (.event-cat), and the
   students age/night lines (.st-range).
   The last holdout was /homepage-alt/, a retired prototype using .v2-eyebrow
   from home-alt.css; both were deleted 2026-09-10, so no eyebrow of any kind
   remains on the site. */

/* ============================================================
   REVIEW FLAGS — temporary, for the church's review pass
   ============================================================
   Marks content that is OURS or UNRESOLVED and needs the team's
   decision, as opposed to copy they supplied. Highlighted so it
   can't be missed on a read-through, and greppable so it can be
   stripped in one pass before launch:

       grep -rn "review-flag" src/

   .review-flag       inline — a word, a line, a single value
   .review-flag-block a whole section, with a reason label that
                      comes from data-review="..."

   DELETE BOTH RULES AND EVERY USAGE BEFORE LAUNCH. Nothing
   wearing these is finished content. */
.review-flag {
  background: #fdf3c8;
  box-shadow: 0 0 0 2px #fdf3c8;
  border-bottom: 2px dotted #a9791a;
}
.review-flag-block {
  position: relative;
  border-left: 4px solid #e0b33a;
  background: #fdf9ea;
  padding: clamp(1rem, 2vw, 1.5rem);
  border-radius: 0 10px 10px 0;
}
.review-flag-block[data-review]::before {
  content: "NEEDS REVIEW — " attr(data-review);
  display: block; margin-bottom: 0.75rem;
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: #8a6412;
}
/* On dark bands the cream-on-yellow inverts badly. */
.section--navy .review-flag { color: var(--ink); }

/* ============================================================
   HERO
   ============================================================ */
/* Every hero on the site opens with the same air above the headline,
   photo or no photo, so the pages feel like one set rather than a dozen
   slightly different openings.

   The top value mirrors the space *below* the copy block exactly —
   .head-block's margin-bottom (clamp(2rem, 5vw, 3.25rem)) plus this
   padding-bottom (clamp(2rem, 5vw, 3.5rem)) — so the hero copy sits
   optically centred in its own band. Keep the two in step if either moves.

   This started as a .hero--no-media modifier for the heroes whose photo had
   been removed, then became the default once it was clear the tighter
   original spacing only ever looked right when a photo was carrying the
   weight. There is no modifier now; every .hero gets this. */
.hero {
  background: var(--cream);
  padding-top: clamp(4rem, 10vw, 6.75rem);
  padding-bottom: clamp(2rem, 5vw, 3.5rem);
}
.hero-media {
  aspect-ratio: 16 / 7;
  border-radius: 10px;
}

/* .hero-media--video and the base .hero-video rules were removed 2026-09-10
   with the old stacked hero. The video now lives in .hero-stage further down
   this file, which owns its own positioning. .hero-media (the image plate)
   stays — Contact, Give, Dream Center, Kids, Students and the campus pages
   all still use it. */

/* Reduced motion: drop the video and let the poster background show through.
   A looping background video is exactly the kind of motion this setting is
   asking us to stop. Kept here, next to the other hero rules, because it
   applies to whichever hero is in use. */
@media (prefers-reduced-motion: reduce) {
  .hero-video { display: none; }
}

/* ============================================================
   LOCATIONS + CARDS
   ============================================================ */
.loc-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: clamp(1.25rem, 2.5vw, 2rem);
  align-items: stretch;
}

.loc-card {
  background: #fff;
  border-radius: 10px;
  padding: clamp(1.75rem, 3vw, 2.5rem);
  box-shadow: 0 18px 40px rgba(18, 18, 19, 0.08);
}

/* --- locations card --- */
.loc-title {
  margin: 0;
  font-size: clamp(1.5rem, 2.4vw, 1.9rem); font-weight: 800;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--navy);
}
.loc-lead { margin: 0.4rem 0 0; color: var(--slate); font-size: 0.95rem; }

.loc-list { list-style: none; margin: 1.25rem 0 0; padding: 0; }
.loc-item { padding: 1.1rem 0; border-top: 1px solid var(--line); }
.loc-item:first-child { border-top: none; padding-top: 0.5rem; }
.loc-name {
  margin: 0; font-size: 1.15rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.01em; color: var(--navy);
}
.loc-addr { margin: 0.35rem 0 0; color: var(--slate); font-size: 0.9rem; }
.loc-times {
  margin: 0.35rem 0 0; font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--navy);
}
.loc-btn { margin-top: 0.85rem; }

.loc-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.5rem; }

/* --- giving banner (full width) --- */
.loc-giving {
  grid-column: 1 / -1;
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 1.5rem 2.5rem;
}
.giving-body { flex: 1 1 22rem; }
.giving-title {
  margin: 0; font-size: clamp(1.5rem, 2.4vw, 1.9rem); font-weight: 800;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--navy);
}
.giving-copy { margin: 0.6rem 0 0; max-width: 40rem; color: var(--slate); font-style: italic; font-size: 0.95rem; }

/* --- blue cards --- */
.loc-cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}
.promo-card {
  position: relative; overflow: hidden;
  display: flex; align-items: flex-end;
  min-height: clamp(15rem, 22vw, 19rem);
  border-radius: 10px;
  background-size: cover; background-position: center;
  text-decoration: none; color: #fff;
}
.promo-card::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top,
    rgba(18, 18, 19, 0.92) 0%,
    rgba(18, 18, 19, 0.55) 32%,
    rgba(18, 18, 19, 0) 60%);
}
.promo-body {
  position: relative; z-index: 2; width: 100%;
  padding: clamp(1.5rem, 2.4vw, 2rem);
}
.promo-title {
  margin: 0 0 1rem;
  font-size: clamp(1.15rem, 1.7vw, 1.6rem); font-weight: 800;
  line-height: 1.05; text-transform: uppercase; letter-spacing: 0.01em;
  white-space: nowrap;
}
.promo-card .btn { width: 100%; }

@media (max-width: 62rem) {
  .loc-grid { grid-template-columns: 1fr; }
}

/* Once the Get Connected cards stack full width, min-height stops describing
   a shape — a 15rem card on a 390px phone is a letterbox, while the campus
   cards further up the page are 3/2. Same crop for both, so the homepage
   reads as one set of cards rather than two. min-height has to go to zero or
   it wins over the ratio.

   Scoped to .quick-grid so any promo card outside that grid keeps its own
   shape. */
@media (max-width: 34rem) {
  .quick-grid .promo-card { min-height: 0; aspect-ratio: 3 / 2; }
}
@media (max-width: 34rem) {
  .loc-cards { grid-template-columns: 1fr; }
}

/* ============================================================
   CONNECT
   ============================================================ */
.connect-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem;
  list-style: none; margin: 0; padding: 0;
}
.connect-card { display: flex; flex-direction: column; gap: 0; }
.connect-media {
  aspect-ratio: 3 / 4;
  border-radius: var(--radius) var(--radius) 0 0;
  display: flex; align-items: flex-end;
}
.connect-media::before {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top, rgba(36,49,60,0.85), rgba(36,49,60,0.1) 55%);
}
.connect-label {
  position: relative; z-index: 2;
  padding: 1.5rem;
  color: var(--cream);
  font-size: 1.3rem; font-weight: 800; line-height: 1.2; text-transform: uppercase;
  letter-spacing: 0.01em;
}
.connect-card .btn { border-radius: 0 0 var(--radius) var(--radius); }

/* ============================================================
   WATCH NOW
   ============================================================ */
.watch { background: #eae7e1; }

.sermon {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(1.75rem, 3.5vw, 3rem);
  align-items: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: clamp(1.5rem, 2.5vw, 2rem);
}
.sermon-media {
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  display: grid; place-items: center;
}
.sermon-body {
  display: flex; flex-direction: column; justify-content: center;
  padding-block: clamp(0.5rem, 1.5vw, 1.25rem);
}
.sermon-meta { margin: 0; color: var(--slate); font-size: 0.95rem; font-weight: 700; }
.sermon-date { margin: 0.3rem 0 0; color: var(--muted); font-size: 0.9rem; }
.sermon-title {
  margin: 1.15rem 0 0;
  font-size: clamp(1.4rem, 2.4vw, 2rem); font-weight: 800;
  line-height: 1.15; color: var(--navy);
}
.sermon-series {
  display: block; margin-top: 0.5rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted);
}
.sermon-desc { margin: 1rem 0 0; color: var(--slate); font-size: 1rem; line-height: 1.6; }
.sermon-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }

@media (max-width: 48rem) {
  .sermon { grid-template-columns: 1fr; }
}

/* scrim so play buttons stay visible over photos */
.sermon-media::before,
.soon-media::before {
  content: ""; position: absolute; inset: 0;
  background: rgba(18, 18, 19, 0.28);
}
.play {
  width: 60px; height: 60px; border-radius: 50%;
  background: rgba(255,255,255,0.95); position: relative; z-index: 1;
}
.play::after {
  content: ""; position: absolute; top: 50%; left: 54%; transform: translate(-50%, -50%);
  border-style: solid; border-width: 11px 0 11px 18px; border-color: transparent transparent transparent var(--navy);
}
.play--sm { width: 42px; height: 42px; }
.play--sm::after { border-width: 8px 0 8px 13px; }

/* ============================================================
   AGES & STAGES
   ============================================================ */
.ages-head { text-align: center; max-width: 46rem; margin-inline: auto; }
.ages-copy { margin: 1.1rem 0 0; color: var(--slate); font-size: 1rem; line-height: 1.6; }
/* The three pillars as one short banner (2026-09-16). Replaces a grid of
   three tall .promo-card--tall cards. The photo sits under an even scrim, not
   the promo cards' bottom-up gradient: here the copy is centred and runs
   across the middle, so the whole image needs darkening to stay legible. */
/* Height: about twice the content's natural ~310px at desktop (Bryan,
   2026-09-16), so more of the photo shows around the copy. min-height rather
   than more padding, with the copy centred vertically inside it, so the extra
   space splits evenly above and below. On phones the wrapped copy is already
   taller than the floor, so the card just fits its content there. */
.pillars-card {
  position: relative; overflow: hidden; isolation: isolate;
  display: flex; align-items: center; justify-content: center;
  min-height: clamp(24rem, 42vw, 39rem);
  border-radius: 10px;
  background-size: cover; background-position: center;
  padding: clamp(2.25rem, 4.5vw, 3.5rem) clamp(1.5rem, 5vw, 4rem);
  text-align: center; text-decoration: none; color: #fff;
}
.pillars-card::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: rgba(18, 18, 19, 0.72);
  transition: background-color 0.18s ease;
}
.pillars-card:hover::before { background: rgba(18, 18, 19, 0.78); }
.pillars-body { width: 100%; max-width: 46rem; margin-inline: auto; }
.pillars-title {
  margin: 0;
  font-size: clamp(1.15rem, 2.2vw, 1.6rem); font-weight: 800;
  line-height: 1.2; text-transform: uppercase; letter-spacing: 0.02em;
}
.pillars-copy {
  margin: 1rem 0 1.5rem;
  font-size: 1rem; line-height: 1.7;
  color: rgba(255, 255, 255, 0.9);
}

/* ============================================================
   EVENTS
   ============================================================ */
.events { background: #eae7e1; }
.events-head {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 1.5rem 2rem; flex-wrap: wrap;
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}
.events-head-text { max-width: 42rem; }
.events-lead { margin: 1rem 0 0; color: var(--slate); font-size: 1rem; line-height: 1.6; }

.events-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
}

/* Weekly rhythms — the "You Don't Have to Wait for an Event." block.
   Moved out of events.css 2026-09-10, when /plan-your-visit/ picked the
   section up too: extraCss takes a single sheet, so a component used by two
   pages has to live in the shared one. Markup is
   partials/weekly-rhythms.njk. */
.evt-rhythms {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: clamp(1rem, 2.5vw, 1.75rem);
}
.evt-rhythm {
  display: flex; flex-direction: column;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line-light);
  border-radius: 10px;
  padding: clamp(1.5rem, 2.6vw, 2.15rem);
}
.evt-rhythm-day {
  margin: 0; font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--red);
}
.evt-rhythm-title {
  margin: 0.75rem 0 0;
  font-size: clamp(1.2rem, 1.9vw, 1.5rem); font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.02em; color: var(--cream);
}
/* short factual line (times, age ranges) — currently all placeholders */
.evt-rhythm-fact {
  margin: 0.6rem 0 0;
  font-size: 0.8rem; font-weight: 700; letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.72);
}
.evt-rhythm-copy {
  margin: 0.85rem 0 0;
  font-size: 0.95rem; line-height: 1.7; color: rgba(255, 255, 255, 0.8);
}
.evt-rhythm-times {
  list-style: none; margin: 1.25rem 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.5rem;
}
.evt-rhythm-times li {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem 1rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line-light);
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: rgba(255, 255, 255, 0.9);
}
.evt-rhythm-times li:last-child { border-bottom: 0; padding-bottom: 0; }
.evt-rhythm-times span { color: var(--muted); font-weight: 600; }
.evt-rhythm-link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin-top: auto; padding-top: 1.5rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--cream); text-decoration: none;
}
.evt-rhythm-link span { font-size: 1.1rem; line-height: 0; transition: transform 0.18s ease; }
.evt-rhythm-link:hover { color: #fff; }
.evt-rhythm-link:hover span { transform: translateX(3px); }

@media (max-width: 62rem) { .evt-rhythms { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 48rem) { .evt-rhythms { grid-template-columns: 1fr; } }


.event-card {
  display: flex; flex-direction: column;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 18px 40px rgba(18, 18, 19, 0.09);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.event-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 26px 52px rgba(18, 18, 19, 0.14);
}
/* 16/9 to match the artwork. Every event graphic the church makes is a
   widescreen promo slide — 1920x1080, 750x422, 720x405 — so a 4/3 frame
   cropped the sides off and cut the title in half ("MEN'S BREAKFAS…").
   Matching the source ratio means `cover` has nothing left to crop. */
.event-media {
  position: relative; display: block;
  aspect-ratio: 16 / 9; overflow: hidden;
}
.event-date {
  position: absolute; top: 0.9rem; right: 0.9rem;
  background: #fff; border-radius: 8px;
  padding: 0.55rem 0.85rem; text-align: center; line-height: 1.15;
  box-shadow: 0 10px 24px -14px rgba(18, 18, 19, 0.55);
}
.event-dow { display: block; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--slate); }
.event-day { display: block; font-size: 1.65rem; font-weight: 800; color: var(--navy); }
.event-month { display: block; font-size: 0.72rem; font-weight: 600; color: var(--slate); }

/* The body is a column that fills whatever height the card has, so the link
   at its end can be pushed to the bottom. Grid rows already stretch cards to
   equal height; without this the body only grew to fit its text and the link
   floated at a different height in every card. */
.event-body {
  padding: 1.35rem clamp(1.1rem, 2vw, 1.6rem) 1.6rem;
  display: flex; flex-direction: column; flex: 1;
}
/* Unused since 2026-09-10 — the badge was removed from the cards but the
   rule is kept because the removal was "for now". Delete both together if it
   turns out not to be coming back. */
.event-cat {
  display: inline-block;
  background: rgba(18, 18, 19, 0.07); color: var(--navy);
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.35rem 0.7rem; border-radius: 6px;
}
.event-title { margin: 0.9rem 0 0; font-size: clamp(1.2rem, 1.8vw, 1.5rem); font-weight: 800; color: var(--navy); line-height: 1.2; }
.event-loc { margin: 0.35rem 0 0; color: var(--slate); font-size: 0.95rem; }
.event-desc { margin: 0.8rem 0 0; color: var(--slate); font-size: 0.95rem; line-height: 1.6; }
.event-link {
  /* margin-top:auto eats the leftover space above it, pinning the link to the
     bottom of the card. The min-height keeps a gap when the copy is long
     enough that there is no slack left. */
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin-top: auto; padding-top: 1.1rem;
  align-self: flex-start;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--navy); text-decoration: none;
}
.event-link span { font-size: 1.1rem; line-height: 0; transition: transform 0.18s ease; }
.event-link:hover span { transform: translateX(3px); }

@media (max-width: 62rem) {
  .events-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 40rem) {
  .events-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   ABOUT / OUR STORY
   ============================================================ */
.about { background: #f4f2ee; }
.about-story {
  text-align: center; max-width: 46rem;
  margin: 0 auto clamp(3rem, 6vw, 4.5rem);
}
.about-lead { margin: 1.2rem 0 0; color: var(--slate); font-size: 1.05rem; line-height: 1.75; }

.about-pastor {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 4vw, 3.5rem); align-items: center;
}
.about-pastor-media { aspect-ratio: 4 / 3; border-radius: 12px; }
.about-pastor-name {
  margin: 0.6rem 0 0;
  font-size: clamp(1.5rem, 2.5vw, 2rem); font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.02em; color: var(--navy);
}
.about-pastor-copy { margin: 1.1rem 0 0; color: var(--slate); font-size: 1rem; line-height: 1.7; }
.about-pastor-copy + .btn { margin-top: 1.75rem; }

@media (max-width: 48rem) {
  .about-pastor { grid-template-columns: 1fr; }
}

/* ============================================================
   MESSAGE / SERMON CARDS
   Shared by /watch/, /sermons/ and the "More Sermons" row on each
   sermon page. Lives here rather than in watch.css because a page
   loads exactly one extraCss file — see the note in watch.css.
   ============================================================ */
/* --- message cards -----------------------------------------------
   Deliberately flatter than .event-card: no card shell, no shadow,
   no white box. Just the thumbnail, then text on the section
   background — so the archive reads as a video grid and never gets
   confused with the events row. --- */
.msg-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.75rem, 3.5vw, 2.75rem) clamp(1.5rem, 3vw, 2.5rem);
}
.msg-card { display: flex; flex-direction: column; }

.msg-media {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  border-radius: 10px;
}
/* scrim so the play button stays legible over any thumbnail —
   style.css only scrims .sermon-media and .soon-media */
.msg-media::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(18, 18, 19, 0.34);
  transition: background 0.2s ease;
}
.msg-media .play { transition: transform 0.2s ease; }
.msg-card:hover .msg-media::before { background: rgba(18, 18, 19, 0.5); }
.msg-card:hover .msg-media .play { transform: scale(1.08); }

.msg-body { padding-top: 1.1rem; }
.msg-series {
  margin: 0;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--red);
}
.msg-title {
  margin: 0.55rem 0 0;
  font-size: clamp(1.1rem, 1.7vw, 1.35rem);
  font-weight: 800;
  line-height: 1.25;
  color: var(--navy);
}
.msg-title a { text-decoration: none; color: inherit; }
.msg-card:hover .msg-title a { text-decoration: underline; text-underline-offset: 3px; }
.msg-meta { margin: 0.5rem 0 0; font-size: 0.9rem; color: var(--slate); }
.msg-dot { padding-inline: 0.4rem; color: var(--muted); }

@media (max-width: 62rem) { .msg-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 40rem) { .msg-grid { grid-template-columns: 1fr; } }

/* ============================================================
   GIVING
   ============================================================ */
.giving-cta {
  position: relative;
  padding-block: clamp(4rem, 9vw, 7rem);
  background-size: cover; background-position: center;
  color: var(--cream);
}
.giving-cta::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(rgba(18, 18, 19, 0.8), rgba(18, 18, 19, 0.84));
}
/* Image-free variant of the closing CTA, for pages where no photo is
   specific enough to earn the slot. Solid navy, no overlay. */
.giving-cta--plain { background-color: var(--navy); background-image: none; }
.giving-cta--plain::before { content: none; }

/* Light variant of the closing CTA, for pages whose last section is already
   dark — /students/ ends on the navy Young Adults block, so a navy CTA
   underneath read as one continuous slab with a heading floating in it.
   Same shape and padding, inverted colours. */
.giving-cta--light {
  background-color: #eae7e1;
  background-image: none;
  color: var(--ink);
}
.giving-cta--light::before { content: none; }
.giving-cta--light .head { color: var(--navy); }
.giving-cta--light .giving-cta-copy { color: var(--slate); }

.giving-cta-inner {
  position: relative; z-index: 1;
  max-width: 46rem; margin-inline: auto; text-align: center;
}
.giving-cta-copy {
  margin: 1.35rem auto 2.25rem;
  color: rgba(255, 255, 255, 0.9);
  font-size: 1.05rem; line-height: 1.75;
}

/* ============================================================
   IGNITE
   ============================================================ */
.ignite-inner { text-align: center; max-width: 52rem; margin-inline: auto; }
.ignite-copy { margin: 1.5rem auto 2rem; color: var(--slate); font-size: 1rem; max-width: 40rem; }

/* ============================================================
   APP
   ============================================================ */
.app-card {
  background: var(--navy); color: var(--cream);
  border-radius: 14px;
  padding: clamp(2rem, 5vw, 3.5rem);
  display: grid; grid-template-columns: 1.2fr 0.8fr; gap: clamp(2rem, 5vw, 3rem);
  align-items: center;
}
.app-copy p { margin: 1.25rem 0 0; color: rgba(255,255,255,0.82); font-size: 0.98rem; }
.app-actions { display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 2rem; }
/* THE BUG THIS FIXES: this was `display: flex` with the default
   `align-items: stretch`, so the phone was stretched to the height of the
   grid row — which the copy column sets, not the phone. An empty grey
   rectangle survived that; a real screenshot did not. `cover` cropped it into
   a tall distorted sliver. Centring the items lets aspect-ratio do its job.

   isolation: isolate gives this its own stacking context so the rear phone
   can sit behind the front one WITHOUT falling behind the card's navy
   background, which is what z-index: -1 was doing to it. */
.app-visual {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 340px;
}
/* EXPLICIT WIDTH *AND* HEIGHT — deliberately not aspect-ratio.
   `align-items: stretch` only applies when the cross size is `auto`, and an
   aspect-ratio computes an automatic height, so it loses to the stretch every
   time. That is why this frame kept coming out 180x706 instead of 180x380:
   the flex container handed it the height of the grid row, which the copy
   column sets. A height in px cannot be stretched — stretch has nothing to
   resolve. align-self makes it immune to the container's align-items too, so
   this survives someone changing .app-visual later. */
.app-phone {
  flex: none;
  align-self: center;
  width: 150px;
  height: 317px;                /* 150 x 19/9, the real 9:19 of a phone */
  border-radius: 22px; border: 6px solid #10171d;
  box-shadow: 0 24px 48px -22px rgba(0,0,0,0.6);
}
/* The screenshot is 9:19.5 against the frame's 9:19, so it is cropped rather
   than squashed, anchored to the top — the status bar and nav read as a real
   phone, and the crop comes off the bottom where the tab bar repeats. */
.app-phone--shot {
  display: block; object-fit: cover; object-position: top center;
  background: #10171d;
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer { background: var(--navy); color: rgba(255,255,255,0.75); padding-top: clamp(3rem, 6vw, 5rem); }
.footer-grid {
  display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr 1.2fr; gap: 2rem;
  padding-bottom: 3rem;
}
.wordmark--footer { font-size: 1.5rem; color: var(--cream); }
/* Logo and contact block share column one, logo on top; the link columns span
   both rows so their headings stay level with the logo, as they were when the
   logo lived inside .footer-brand. */
.footer-logo { grid-column: 1; grid-row: 1; display: inline-flex; align-self: start; }
.footer-logo img { height: 56px; width: auto; }
.footer-brand { grid-column: 1; grid-row: 2; }
.footer-col { grid-row: 1 / 3; }
.footer-meta { margin: 1.25rem 0 0; font-size: 0.82rem; line-height: 1.8; }
.footer-brand .footer-meta { margin-top: 0; }
.footer-meta a { color: var(--beige); }
.footer-heading { margin: 0 0 1.1rem; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; color: var(--cream); }
.footer-col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.6rem; }
.footer-col a { text-decoration: none; color: rgba(255,255,255,0.75); font-size: 0.88rem; }
.footer-col a:hover { color: var(--cream); }

.footer-legal {
  border-top: 1px solid var(--line-light);
  padding-block: 1.5rem;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
.footer-legal p { margin: 0; font-size: 0.78rem; }
/* Build credit. Italic and a notch dimmer than the copyright so it reads as a
   sign-off rather than a second piece of site business. */
.footer-credit { font-style: italic; color: rgba(255,255,255,0.55); }
.footer-credit a { color: var(--beige); text-decoration: none; }
.footer-credit a:hover { color: var(--cream); text-decoration: underline; }
/* Social icons. Sized once here rather than per-icon, so all three are
   identical however different their source paths are — the old FB / IG / YT
   text labels rendered at three different widths. 18px keeps them clearly
   secondary to the footer links; they are a courtesy, not a call to action. */
.footer-social { display: flex; align-items: center; gap: 1.1rem; }
.footer-social a {
  display: inline-flex; align-items: center; justify-content: center;
  color: rgba(255, 255, 255, 0.7);
  transition: color 0.18s ease;
}
.footer-social a:hover { color: var(--cream); }
.footer-social svg { width: 18px; height: 18px; display: block; }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 900px) {
  .join-grid, .soon-grid, .app-card { grid-template-columns: 1fr; }
  .connect-grid { grid-template-columns: 1fr; max-width: 26rem; margin-inline: auto; }
  .watch-feature { grid-template-columns: 1fr; }
  .watch-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  /* Two columns, so drop the desktop row placement and let everything flow in
     document order — the logo taking a row of its own across the full width. */
  .footer-logo { grid-column: 1 / -1; grid-row: auto; }
  .footer-brand { grid-column: auto; grid-row: auto; }
  .footer-col { grid-row: auto; }
  .app-visual { order: -1; }
}

@media (max-width: 560px) {
  .watch-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  /* On a phone a 56px-tall wordmark stranded over one half-width column reads
     as cramped, so let it fill the footer's width instead. The intrinsic
     width/height on the <img> keeps the height reserved before it loads. */
  .footer-logo { display: block; width: 100%; }
  .footer-logo img { width: 100%; height: auto; }
  .head-block--row { flex-direction: column; align-items: flex-start; }
  .footer-legal { flex-direction: column; align-items: flex-start; }
  .utility-logo img { height: 42px; }
}

/* ============================================================
   CENTERED ACTION ROW
   ============================================================ */
.cta-row {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: 0.75rem; margin-top: 1.75rem;
}

/* ============================================================
   CAMPUS CARD
   Shared by the homepage "Find a Campus" row and the campus
   chooser on Plan Your Visit.
   ============================================================ */
.campus-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(1rem, 2vw, 1.5rem);
}
.campus-card {
  display: flex; flex-direction: column;
  background: #fff; border-radius: 10px; overflow: hidden;
  box-shadow: 0 18px 40px rgba(18, 18, 19, 0.08);
}
.campus-media { aspect-ratio: 3 / 2; }
.campus-body {
  display: flex; flex-direction: column; flex: 1;
  padding: clamp(1.25rem, 2vw, 1.6rem);
}
.campus-name {
  margin: 0; font-size: 1.15rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.02em; color: var(--navy);
}
.campus-addr { margin: 0.5rem 0 0; font-size: 0.9rem; line-height: 1.6; color: var(--slate); }
.campus-times {
  list-style: none; margin: 0.85rem 0 1.35rem; padding: 0;
  display: flex; flex-direction: column; gap: 0.25rem;
}
.campus-times li {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--navy);
}
/* pins the CTA to the card bottom so buttons align across cards
   even when a campus has more service times than its neighbours */
.campus-body > .btn { margin-top: auto; }

@media (max-width: 62rem) { .campus-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 34rem) { .campus-grid { grid-template-columns: 1fr; } }

/* ============================================================
   QUICK ACCESS ROW (4-up promo cards)
   ============================================================ */
.quick-grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(1rem, 2vw, 1.5rem);
}
@media (max-width: 62rem) { .quick-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 34rem) { .quick-grid { grid-template-columns: 1fr; } }

/* ============================================================
   HERO — VIDEO BACKGROUND WITH OVERLAID COPY
   The homepage hero. Chosen 2026-09-10 over the earlier treatment
   that stacked copy above a 16:7 video plate.

   Deliberately boring layout: no aspect-ratio, no grid centring.
   The first attempt used both plus min-height and the copy ended up
   flowing UNDER the video instead of over it — white text on the cream
   page, invisible. Here the box is sized by its own padding, the video
   and scrim are absolutely positioned behind it, and every layer has an
   explicit z-index. Content can only ever make the box taller, never
   escape it.
   ============================================================ */
/* A gap between the header and the video block. This was 0, which butted the
   video straight against the header's bottom border and made the two read as
   one welded slab. Much smaller than the default .hero padding-top, since the
   video is a contained rounded block rather than copy that needs air above
   it. Currently 60px on desktop (3.75rem), easing to 32px on narrow screens.
   Tune this one value to change the gap. */
.hero--overlay { padding-top: clamp(2rem, 4.5vw, 3.75rem); }

/* Homepage: one padding value, everywhere (Bryan, 2026-09-16).
   Every section's top and bottom now match the gap above the hero video, so
   the space inside each alternating band is identical and the video sits
   evenly between the header and the first band. Before: 60px above the
   video, 56px below it, 104px in every section, 112px in the photo CTA.

   Done by redefining --section-y for this page only, so .section needs no
   change. The hero and the photo CTA set their own padding rather than
   reading the variable, so they are pointed at it explicitly. Other pages
   keep the site-wide 104px. */
.page-home { --section-y: clamp(2rem, 4.5vw, 3.75rem); }
.page-home .hero--overlay,
.page-home .giving-cta { padding-block: var(--section-y); }

.hero-stage {
  position: relative;
  overflow: hidden;
  border-radius: 10px;
  isolation: isolate;          /* keeps these z-indexes to themselves */
  display: flex;
  align-items: center;
  justify-content: center;
  /* Height comes from this padding plus the copy — it can't crop the text
     however long the copy runs. Raised 2026-09-10 (Bryan: "a tad taller"):
     padding 8rem -> 10.5rem and the floor 34rem -> 40rem, roughly +13% on a
     desktop viewport. Adjust these two numbers to retune the height; nothing
     else controls it. */
  padding-block: clamp(5.5rem, 13vw, 10.5rem);
  min-height: 40rem;
}
.hero-stage .hero-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  z-index: 0;
}
/* Scrim. Without it the copy is unreadable the moment the footage cuts to a
   bright frame, and this footage does. Heavier through the middle where the
   text sits so the edges still read as video. */
.hero-stage::after {
  content: "";
  position: absolute; inset: 0;
  z-index: 1;
  background: radial-gradient(
    ellipse at center,
    rgba(18, 18, 19, 0.74) 0%,
    rgba(18, 18, 19, 0.56) 55%,
    rgba(18, 18, 19, 0.46) 100%
  );
}
.hero-stage-inner {
  position: relative;
  z-index: 2;
  max-width: 46rem;
  padding-inline: clamp(1rem, 4vw, 2.5rem);
  text-align: center;
  /* Explicit, so nothing inherits the page's dark ink onto dark footage. */
  color: var(--cream);
}
.hero-stage-title { margin: 0; }
.hero-stage-sub {
  margin: 1.1rem 0 0;
  font-size: 0.98rem;
  line-height: 1.75;
  color: rgba(255, 255, 255, 0.92);
}
.hero-stage .cta-row { margin-top: 2rem; }

@media (max-width: 48rem) {
  /* A wide box crops this footage hard on a phone; more height, less width. */
  .hero-stage { min-height: 34rem; padding-block: clamp(4rem, 15vw, 7rem); }
}

/* Honeypot wrapper for the Netlify forms — a decoy field bots fill in and
   humans never see. Not .sr-only: that stays readable to screen readers,
   and this field should be hidden from everyone real. */
.visually-hidden { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ============================================================
   404
   ============================================================ */
.nf-code {
  margin: 0 0 0.75rem;
  font-size: clamp(2.5rem, 7vw, 4.5rem);
  font-weight: 800;
  letter-spacing: 0.08em;
  color: var(--line);          /* barely there — it labels, it doesn't shout */
  line-height: 1;
}
.nf-links {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(1rem, 2vw, 1.5rem);
}
.nf-link {
  display: flex; flex-direction: column; height: 100%;
  background: #fff; border: 1px solid var(--line); border-radius: 10px;
  padding: clamp(1.35rem, 2.2vw, 1.75rem);
  text-decoration: none;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.nf-link:hover { transform: translateY(-3px); box-shadow: 0 20px 40px -24px rgba(18,18,19,0.5); }
.nf-link-label {
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--red);
}
.nf-link-title {
  margin-top: 0.6rem;
  font-size: clamp(1.1rem, 1.7vw, 1.3rem); font-weight: 800;
  color: var(--navy); line-height: 1.2;
}
.nf-link-go {
  margin-top: auto; padding-top: 1.1rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--slate);
}
.nf-home { margin: clamp(2rem, 4vw, 3rem) 0 0; text-align: center; }

@media (max-width: 62rem) { .nf-links { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 34rem) { .nf-links { grid-template-columns: 1fr; } }
