/* =====================================================================
   Pilgrimage Media
   Plain CSS. Every value here was measured off the live site with
   getComputedStyle at 1920/1440/992/768/390 — see the Obsidian note
   "Pilgrimage Media - Brand System" and docs/research/ in this repo.
   Safe to hand-edit; there is no build step.
   ===================================================================== */

/* fonts.css is NOT @import-ed here. An @import cannot begin downloading until
   this file has arrived and been parsed, so it serialises the font CSS — and
   therefore the fonts themselves — one round trip behind the stylesheet. Every
   page links both sheets directly in <head> instead, so they fetch in parallel.
   If you add a page, link /css/fonts.css or it will render in Times. */

/* ---------------------------------------------------------------------
   Tokens
   --------------------------------------------------------------------- */
:root {
  --dark: #393939;   /* rgb(57,57,57)    */
  --olive: #696d5e;  /* rgb(105,109,94)  */
  --white: #ffffff;
  --black: #000000;
  --highlight: #c4aa84;  /* hover/highlight accent — Mike's pick, 2026-07-23 */

  /* Squarespace Fluid Engine geometry. Gap is a flat 11px at every
     viewport; the outer gutter column is (gutter - gap) wide so that
     gutter-col + gap lands exactly on the page gutter. */
  --fe-gap: 11px;
  --fe-gutter: 6vw;   /* < 768px */
  --fe-row: 24px;     /* < 768px, Squarespace's mobile row unit */
  --fe-pad-y: 9px;    /* .content-wrapper inset, < 768px */

  /* Diagonal divider depth: a flat 6vw at every viewport
     (23.4px @390 · 46.08 @768 · 59.52 @992 · 86.4 @1440 · 115.2 @1920). */
  --divider: 6vw;

  /* Fluid type. Between 768 and 1440 every heading level is
     `16px + N·vw` — the 16px intercept is identical at every level,
     which is why these are calc() and not clamp(). Below 768 the scale
     is fixed, and is LARGER than at 768. */
  --fs-h1: 48.384px;
  --fs-h2: 35.4px;
  --fs-h3: 29px;
  --fs-em: 22.5px;
  --fs-wordmark: 21.4px;

  /* Hero top padding = the absolutely-positioned header's own height.
     The logo swap made the wordmark taller than the single text line
     this was originally measured against — padded out by the delta
     (logo height 5.8x minus the ~1.2x line-height it replaced) so hero
     content still clears the header at every breakpoint. */
  --header-h: calc(37px + 12.011vw + var(--fs-wordmark) * 4.6);
}

@media (min-width: 768px) {
  :root {
    --fe-gutter: 4vw;
    --fe-row: 1.979vw;  /* 28.5px @1440 */
    --fe-pad-y: 1vw;    /* 14.4px @1440 */
    --fs-h1: calc(16px + 3.601vw);
    --fs-h2: calc(16px + 2.158vw);
    --fs-h3: calc(16px + 1.429vw);
    --fs-em: calc(16px + 0.729vw);
    --fs-wordmark: calc(16px + 0.595vw);
  }
}

/* 800px is where the nav un-collapses and the header padding changes.
   Recalibrated 2026-07-23: the old formula's 6.11vw term grew faster than
   the header itself (header padding is only 2.8vw — 1.4vw top + bottom),
   so the dead space below the header widened on large screens (~30px gap
   at 1440, ~63px at 2560). This version tracks the header's actual build
   — 2.8vw padding + logo height (fs-wordmark * 5.8) + nav row — plus a
   flat 6px breathing gap that no longer grows with viewport width. */
@media (min-width: 800px) {
  :root { --header-h: calc(40px + 2.8vw + var(--fs-wordmark) * 5.8); }
}

@media (min-width: 1440px) {
  :root {
    --fs-h1: calc(39.6px + 1.958vw);
    --fs-h2: calc(30.3px + 1.167vw);
    --fs-h3: calc(25.3px + 0.792vw);
    --fs-em: calc(21px + 0.375vw);
    --fs-wordmark: calc(19.8px + 0.333vw);
  }
}

/* ---------------------------------------------------------------------
   Base
   --------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* Smooth scrolling only for visitors who haven't asked to reduce motion. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  background: var(--dark);
  color: var(--white);
  /* Nunito — the reading voice: body copy, paragraphs, form fields.
     Nav/labels/buttons override to Lexend below; headings override to
     Gelasio. */
  font-family: "Nunito", sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  letter-spacing: normal;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: "Gelasio", serif;
  font-weight: 400;
  margin: 0;
}
h1 { font-size: var(--fs-h1); line-height: 1.056; }
h2 { font-size: var(--fs-h2); line-height: 1.114; }
h3 { font-size: var(--fs-h3); line-height: 1.143; }

p { margin: 0; }
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; letter-spacing: inherit; }

/* ---------------------------------------------------------------------
   Fluid Engine grid
   26 columns at >=768px (2 gutters + 24 content), 10 below (2 + 8).
   Rows are minmax(_, auto) so a row grows when text wraps to more lines
   than the reference viewport did — fixed heights collide at in-between
   widths. Row COUNT is set per section: the live site declares it
   explicitly and includes trailing empty rows that pad section height.
   --------------------------------------------------------------------- */
.fe-grid {
  display: grid;
  position: relative;
  gap: var(--fe-gap);
  grid-template-columns:
    calc(var(--fe-gutter) - var(--fe-gap))
    repeat(8, 1fr)
    calc(var(--fe-gutter) - var(--fe-gap));
  grid-template-rows: repeat(var(--fe-rows, 1), minmax(var(--fe-row), auto));
  grid-auto-rows: minmax(var(--fe-row), auto);
}
@media (min-width: 768px) {
  .fe-grid {
    grid-template-columns:
      calc(var(--fe-gutter) - var(--fe-gap))
      repeat(24, 1fr)
      calc(var(--fe-gutter) - var(--fe-gap));
  }
}
.fe-grid > * { min-width: 0; }

/* Squarespace's .content-wrapper vertical inset. Present on hero,
   mission, latest-posts and footer — NOT on the two full-bleed-photo
   sections (portfolio, services). */
.fe-pad { padding-top: var(--fe-pad-y); padding-bottom: var(--fe-pad-y); }

/* ---------------------------------------------------------------------
   Sections + diagonal dividers

   The real mechanism: each section holds an absolutely-positioned
   background layer, inset -6vw at the top so it BLEEDS UPWARD into the
   previous section, clipped at its own bottom by a 6vw diagonal.
   Sections then carry DESCENDING z-index (6,5,4,3,auto) so an earlier
   section paints OVER the next — the triangle its clip removes is what
   reveals the next section's bled-up background. Without the descending
   z-index every seam renders flat.
   --------------------------------------------------------------------- */
.section { position: relative; padding-bottom: var(--divider); }
.section--no-pad { padding-bottom: 0; }

.section-border {
  position: absolute;
  inset: calc(-1 * var(--divider)) 0 0;
  z-index: 0;
  overflow: hidden;
}
/* The hero is first in the document, so it has nothing to bleed into. */
.section-border--flush { inset: 0; }

.section-border > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Inlined (not <img src>) so it always renders as real vector paths, even
   inside this absolutely-positioned, clip-path'd layer — some browsers
   rasterize an external SVG referenced via <img> to a low-res bitmap once
   it sits behind a clip-path, which looked pixelated at hero size.
   preserveAspectRatio="xMidYMid slice" is the SVG-native equivalent of
   object-fit: cover. */
.hero__topo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Bottom edge drops left→right. */
.divider-down { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - var(--divider))); }
/* Bottom edge rises left→right. */
.divider-up   { clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--divider)), 0 100%); }

.section-content { position: relative; z-index: 1; }

.bg-dark  { background: var(--dark); }
.bg-olive { background: var(--olive); }
.bg-white { background: var(--white); }
.on-dark  { color: var(--white); }
.on-light { color: var(--black); }

/* Squarespace's 1px rule block. Colour comes from currentColor so the
   same class is white in Portfolio and black in Services. */
.rule { height: 1px; border: 0; margin: 8px 0; background: currentColor; }

/* ---------------------------------------------------------------------
   Buttons
   Size comes from the grid area, NOT a fixed height: the same button is
   212x68 in Portfolio and 323x68 in Latest Posts at 1440, and 59px tall
   at 390 — in each case exactly the span of its area.
   --------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 34.736px;
  font-family: "Lexend", sans-serif;
  font-size: 16px;
  font-weight: 500;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
}
/* Outline, uppercase, 0.1em tracking — the Portfolio CTA. */
.btn-outline {
  border: 1px solid var(--white);
  background: transparent;
  color: var(--white);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  transition: background-color 0.1s linear, color 0.1s linear;
}
.btn-outline:hover { background: var(--white); color: var(--black); }
/* Solid olive, mixed case, NORMAL tracking — Services + "More posts". */
.btn-solid {
  border: 0;
  background: var(--olive);
  color: var(--white);
  text-transform: none;
  letter-spacing: normal;
  transition: opacity 0.1s linear;
}
.btn-solid:hover { opacity: 0.8; }

/* ---------------------------------------------------------------------
   Header — absolute, transparent, NOT sticky. Computed styles at scroll
   0 and 800 are byte-identical on the live site; do not add a scroll
   behaviour.
   --------------------------------------------------------------------- */
.site-header {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 10;
  width: 100%;
  color: var(--white);
}
.site-header__inner { position: relative; padding: 6vw; }
@media (min-width: 800px) { .site-header__inner { padding: 1.4vw 4vw; } }

.wordmark {
  display: block;
  width: 100%;
  text-align: center;
}

.wordmark__logo {
  display: inline-block;
  height: calc(var(--fs-wordmark) * 5.8);
  width: auto;
}

/* Logo art is white-on-transparent; the light-header pages (Contact) need
   it inverted to black, matching the text-color flip .header-on-light
   already does for the rest of the header. */
.header-on-light .wordmark__logo { filter: invert(1); }

.site-nav { display: none; }
@media (min-width: 800px) {
  .site-nav { display: flex; justify-content: center; width: 100%; }
}
.site-nav ul {
  display: inline-flex;
  justify-content: center;
  margin: 0;
  padding: 0;
  list-style: none;
}
.site-nav li { margin: 0 10.8px; white-space: nowrap; }
.site-nav a {
  display: block;
  padding: 1.6px 0;
  text-align: center;
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  transition: color 0.15s linear;
}
.site-nav a:hover { color: var(--highlight); }

.nav-toggle {
  position: absolute;
  top: 50%;
  right: 6vw;
  transform: translateY(-50%);
  display: flex;
  padding: 0;
  border: 0;
  background: none;
  color: var(--white);
  cursor: pointer;
}
@media (min-width: 800px) { .nav-toggle { display: none; } }

/* Mobile overlay menu */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 32px;
  background: var(--dark);
}
.mobile-menu[hidden] { display: none; }
.mobile-menu ul {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 32px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.mobile-menu a {
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  transition: color 0.15s linear;
}
.mobile-menu a:hover { color: var(--highlight); }
.mobile-menu__close {
  position: absolute;
  top: 6vw;
  right: 6vw;
  padding: 0;
  border: 0;
  background: none;
  color: var(--white);
  cursor: pointer;
}
body.menu-open { overflow: hidden; }

/* ---------------------------------------------------------------------
   Hero
   --------------------------------------------------------------------- */
.hero { z-index: 6; padding-top: var(--header-h); }
/* Zeroes out the .fe-pad top inset just for the hero, so img-1 (already
   pinned to grid row 1, as high as the row system allows) and the heading
   sit right at the top of the grid instead of floating fe-pad-y below it.
   Bottom padding is untouched — that space still leads into the next
   section's divider. */
.hero__grid { --fe-rows: 24; padding-top: 0; }
@media (min-width: 768px) { .hero__grid { --fe-rows: 21; } }

/* object-fit is CONTAIN, not cover — these render at essentially their
   natural intrinsic size. cover crops them and is visibly wrong.

   The img is absolutely positioned so it contributes NO intrinsic height:
   the box is defined purely by its grid area, and the photo letterboxes
   inside it. Left in normal flow, `width:100%` scales each photo up to its
   column and pushes the rows taller — that alone made the hero 232px too
   tall at 1440. */
.hero__figure { position: relative; }
.hero__figure img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
/* The hero's self-hosted background video. `cover`, not `contain` like the
   sibling photos: the clip is 16:9 and this box is not, and cover is what the
   old Vimeo background embed did internally. The poster image is cropped the
   same way, so the handover from poster to first frame doesn't jump.
   No opacity/reveal machinery here any more — a native <video> shows its own
   poster until it can play, which is the whole thing the old JS existed to
   fake for an iframe. */
.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 0;
  pointer-events: none;
}

/* The hero heading is the one block on the page that is vertically CENTRED
   within its grid area (measured offset 28px = (265-210)/2). Every other
   block is top-aligned. Squarespace sets this per block via justify-content
   on the block's inner flex wrapper. */
/* Plain Gelasio, same as every other heading — no more bold uppercase
   Lexend treatment. */
/* The hero headline is the page's <h1> — it is the one line that says what
   this site is, so it should be the document's top heading rather than the
   "View the Galleries" band further down. The type scale is pinned to the h2
   step here so the markup change is invisible: this has always rendered at
   --fs-h2 and still does, at every breakpoint. Same trick in reverse on
   .portfolio__heading / .services__heading below. */
.hero__heading {
  grid-area: 1 / 2 / 7 / 11;
  align-self: center;
  font-size: var(--fs-h2);
  line-height: 1.114;
}
/* "on a journey" — italic Gelasio in the highlight gold. */
.hero__heading em {
  font-style: italic;
  color: var(--highlight);
}
.hero__img-1   { grid-area: 6 / 6 / 16 / 11; }
.hero__img-2   { grid-area: 11 / 1 / 17 / 7; }  /* bleeds into the gutter on purpose */
.hero__img-3   { grid-area: 15 / 5 / 23 / 8; }
@media (min-width: 768px) {
  .hero__heading { grid-area: 1 / 11 / 9 / 21; }
  .hero__img-1   { grid-area: 1 / 2 / 18 / 10; }
  .hero__img-2   { grid-area: 11 / 9 / 20 / 19; }
  .hero__img-3   { grid-area: 7 / 16 / 18 / 24; }
}

/* ---------------------------------------------------------------------
   Portfolio
   --------------------------------------------------------------------- */
.portfolio { z-index: 5; }
.portfolio__grid { --fe-rows: 29; }
@media (min-width: 768px) { .portfolio__grid { --fe-rows: 24; } }

/* Demoted to <h2> (the hero owns the h1 now) but still drawn at the h1 step. */
.portfolio__heading { grid-area: 3 / 2 / 6 / 10; font-size: var(--fs-h1); line-height: 1.056; }
/* .rule's background: currentColor picks up the browser's own UA-default
   gray for <hr> (not the inherited white from .on-dark) unless color is
   set explicitly here. */
.portfolio__rule    { grid-area: 6 / 2 / 7 / 10; background: var(--white); }
/* Upright, tracked Lexend — not italic. Italic is reserved for Gelasio
   display headlines only. */
.portfolio__eyebrow {
  grid-area: 7 / 2 / 8 / 10;
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
}
.portfolio__cat-1   { grid-area: 11 / 2 / 13 / 11; }
.portfolio__cat-2   { grid-area: 14 / 2 / 16 / 11; }
.portfolio__cat-3   { grid-area: 17 / 2 / 19 / 11; }
.portfolio__body    { grid-area: 20 / 2 / 25 / 9; }
.portfolio__cta     { grid-area: 25 / 2 / 27 / 7; }
@media (min-width: 768px) {
  .portfolio__heading { grid-area: 5 / 11 / 9 / 26; }
  .portfolio__rule    { grid-area: 7 / 11 / 8 / 20; }
  .portfolio__eyebrow { grid-area: 8 / 11 / 9 / 20; }
  .portfolio__cat-1   { grid-area: 10 / 11 / 12 / 26; }
  .portfolio__cat-2   { grid-area: 12 / 11 / 14 / 26; }
  .portfolio__cat-3   { grid-area: 14 / 11 / 16 / 26; }
  .portfolio__body    { grid-area: 17 / 11 / 19 / 21; }
  .portfolio__cta     { grid-area: 20 / 11 / 22 / 15; }
}
/* The category anchors are inline on the live site — only as wide as
   their text, not the full block. The live site declares no hover style;
   added one using Mike's highlight color since these are the site's main
   in-content links. */
/* inline-block, not inline: the raised arrow makes an inline run's box
   irregular, so the focus ring traced a notched outline around it. Shrink-to-
   fit keeps the "only as wide as its text" behaviour the live site has. */
.portfolio__cat a {
  display: inline-block;
  transition: color 0.15s linear;
}
.portfolio__cat a:hover { color: var(--highlight); }
.portfolio__cat a:focus-visible {
  outline: 2px solid var(--white);
  outline-offset: 3px;
}

/* Resting affordance for the three category links.

   These are the main doors into the portfolio, and they used to sit over a
   busy black-and-white photo as bare serif headings — nothing said they were
   clickable until the cursor was already on them, which is no help to anyone
   deciding whether to try.

   The rule is drawn as a background gradient rather than a border-bottom
   because the anchors stay `display: inline`: a border cannot animate its
   width, and going inline-flex to get one would change how these wrap at
   narrow widths. background-size IS animatable, and with the default
   box-decoration-break the short rule paints once at the start of the first
   line fragment rather than once per wrapped line. */
.cat-label {
  background-image: linear-gradient(var(--highlight), var(--highlight));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 2.4ch 1px;   /* a stub at rest — scales with the type */
  /* The gradient paints to the bottom of the inline box, which already sits a
     font-descent below the baseline — enough to clear the "p" in Sports. Extra
     padding here reads as a detached dash rather than an underline. */
  padding-bottom: 0.04em;
}
.portfolio__cat a:hover .cat-label,
.portfolio__cat a:focus-visible .cat-label { background-size: 100% 1px; }

.cat-arrow {
  display: inline-block;
  margin-left: 0.42em;
  /* Sat on the baseline it reads low against a 36px serif; lifting it a tenth
     of an em centres it on the x-height. A length, not `middle`, so it tracks
     the fluid type scale. */
  vertical-align: 0.1em;
  color: var(--highlight);
  overflow: visible;            /* the 1px stroke sits on the viewBox edge */
}

/* Growth and nudge are motion; the color changes above are state and stay
   ungated, matching every other transition in this file. */
@media (prefers-reduced-motion: no-preference) {
  .cat-label { transition: background-size 0.22s ease-out; }
  .cat-arrow { transition: transform 0.22s ease-out, color 0.15s linear; }
  .portfolio__cat a:hover .cat-arrow,
  .portfolio__cat a:focus-visible .cat-arrow { transform: translateX(4px); }
}

/* ---------------------------------------------------------------------
   Mission — flat olive, no photo, no gradient.
   --------------------------------------------------------------------- */
.mission { z-index: 4; }
.mission__grid { --fe-rows: 11; }
@media (min-width: 768px) { .mission__grid { --fe-rows: 7; } }

.mission__block { grid-area: 2 / 2 / 11 / 10; }
@media (min-width: 768px) { .mission__block { grid-area: 2 / 9 / 7 / 19; } }

/* Upright, tracked Lexend — italic is reserved for Gelasio display
   headlines (see the mission__heading emphasis words below). */
.mission__eyebrow {
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: var(--fs-em);
  line-height: 1.171;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-align: center;
}
.mission__heading { margin-top: 32px; text-align: center; }

/* ---------------------------------------------------------------------
   Services — first light section. No scrim: the live site's computed
   background-image on the text and its whole ancestor chain is `none`.
   --------------------------------------------------------------------- */
.services { z-index: 3; }
.services__grid { --fe-rows: 32; }
@media (min-width: 768px) { .services__grid { --fe-rows: 20; } }
/* Anchored slightly left of centre rather than at 50%.
   The crop here is height-driven, so as the viewport narrows the photo zooms
   and its subject marches LEFT into the text column — at 992px and below her
   shoes landed under the last service item even after that column was pulled
   in to grid line 11. Narrowing the text further can't fix it (clearing 800px
   would need a ~170px-wide column), but showing 5% more of the photo's empty
   left side moves her right at every width. Measured clearance between the
   text's right edge and her leading shoe: 77px at 1440, 67 at 1280, 56 at
   1100, 48 at 992, 42 at 900, 35 at 800. Don't raise this past ~0.46 or the
   narrow-viewport clearances go negative again. */
.services .section-border > img { object-position: 45% 50%; }

/* Demoted to <h2> (see .portfolio__heading) but still drawn at the h1 step. */
.services__heading { grid-area: 3 / 2 / 5 / 10; font-size: var(--fs-h1); line-height: 1.056; }
.services__rule    { grid-area: 5 / 2 / 6 / 10; }
.services__eyebrow { grid-area: 6 / 2 / 8 / 10; }
.services__body    { grid-area: 8 / 2 / 16 / 9; }
.services__cta     { grid-area: 27 / 3 / 29 / 9; }
@media (min-width: 768px) {
  .services__heading { grid-area: 4 / 2 / 6 / 14; }
  .services__rule    { grid-area: 6 / 2 / 7 / 11; }
  .services__eyebrow { grid-area: 8 / 2 / 9 / 14; }
  /* Ends at line 11, not 14. The background photo's subject lies with her
     shoes reaching in from the right, and at line 14 the last service item's
     line-endings ran across them — dark text on a dark shoe. Line 11 stops the
     column ~37px clear of the shoes, and is where .services__rule already ends,
     so the text now aligns with the rule under "Services" instead of
     overhanging it. Side benefit: the measure drops from ~85 characters to
     ~64, which is inside the comfortable range rather than well past it. */
  .services__body    { grid-area: 9 / 2 / 14 / 11; }
  .services__cta     { grid-area: 15 / 2 / 17 / 7; }
}

/* Upright, tracked Lexend — same eyebrow treatment as Portfolio and
   Mission. */
.services__eyebrow {
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: var(--fs-em);
  line-height: 1.17;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.services__body p { margin: 16px 0; }
.services__body p:first-child { margin: 0 0 16px; }
.services__body p:last-child  { margin: 16px 0 0; }
/* The two lead weights genuinely differ on the live site. */
/* Bold, not italic — Nunito's italic face isn't loaded (italic is
   reserved for Gelasio headlines), so emphasis here is weight only. */
.services__body em { font-style: normal; font-weight: 700; }

/* ---------------------------------------------------------------------
   Client Spotlight — standalone section between Services and Latest
   Posts. Plain white bg (no photo needed): it's the same light "chapter"
   as Services and Posts, so a flat bg-white still needs the divider-up
   clip-path + z-index for the seam to render, even though the color it
   reveals is identical to what's already there.
   --------------------------------------------------------------------- */
.progress { z-index: 2; }
.progress__grid { --fe-rows: 16; }
@media (min-width: 768px) { .progress__grid { --fe-rows: 16; } }

/* Single wrapper for heading + eyebrow + cards, instead of three
   separately-positioned grid items. justify-self: center shrinks the
   wrapper to its own content width (the cards row, its widest child) and
   centers that as a block; align-items: flex-end inside then right-aligns
   heading/eyebrow to that same shrunk width — so their right edge always
   lands exactly on the cards row's right edge (Kalia's photo), wherever
   centering happens to put it, rather than a fixed px guess. */
.progress__inner {
  grid-area: 2 / 2 / 15 / 10;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-self: center;
  gap: 12px;
}
@media (min-width: 768px) {
  .progress__inner { grid-area: 3 / 2 / 16 / 26; gap: 16px; }
}
.spotlight-cards { margin-top: 8px; }

.progress__heading, .progress__eyebrow { text-align: right; }
.progress__eyebrow {
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.7;
  margin: 0;
}
/* Explicit column counts, not a wrapping flexbox.
   With three cards, wrapping and a 3-across grid are the same picture. With
   six they are not: flex-wrap fits as many 280px cards per line as the
   available width allows, which at 1440 is four — leaving a stranded pair
   underneath. Declaring the columns makes every breakpoint an even split
   (3+3, then 2+2+2, then a single stack) instead of one that depends on where
   the viewport happens to fall.

   The tracks cap at 280px and the whole block is centred, so its width is
   still content-derived — that is what .progress__inner's flex-end alignment
   measures to right-align the heading against the last card's edge. */
.spotlight-cards {
  display: grid;
  grid-template-columns: minmax(0, 280px);
  /* Rows read as rows, not as one continuous column of cards: the vertical gap
     is wider than the horizontal one so the eye groups across before down. */
  gap: 44px 32px;
  justify-content: center;
}
@media (min-width: 600px) { .spotlight-cards { grid-template-columns: repeat(2, minmax(0, 280px)); } }
@media (min-width: 1024px) { .spotlight-cards { grid-template-columns: repeat(3, minmax(0, 280px)); } }
/* Each card's own gallery cover photo (see build.mjs's cover image for
   the same slug) rather than a placeholder. */
.spotlight-card__photo {
  aspect-ratio: 4 / 5;
  width: 100%;
  object-fit: cover;
  border: 1px solid rgba(0, 0, 0, 0.12);
  margin-bottom: 12px;
}
.spotlight-card__photo span {
  font-family: "Lexend", sans-serif;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(0, 0, 0, 0.4);
  text-align: center;
  padding: 0 12px;
}
.spotlight-card__type {
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--highlight);
  margin: 0 0 4px;
}
.spotlight-card__name { font-size: var(--fs-em); margin: 0 0 6px; }
.spotlight-card__caption { font-size: 14px; line-height: 1.5; color: inherit; opacity: 0.85; }

/* "More Galleries" — the way out of the curated six to the full set.
   Set as a label (Lexend, tracked, uppercase) so it echoes the eyebrow at the
   top of the section and bookends the block, rather than competing with the
   card names. .progress__inner's align-items: flex-end lands it under the last
   card's right edge, on the same line the heading is aligned to.
   The stub-and-arrow mechanics come from .cat-label / .cat-arrow above; only
   the hover/focus triggers are re-bound, since those are scoped to
   .portfolio__cat. The focus ring is charcoal, not white — this section is on
   a light background, where the portfolio section's white ring is invisible. */
.progress__more {
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 4px 0 0;
}
.progress__more a {
  display: inline-block;
  color: inherit;
  transition: color 0.15s linear;
}
.progress__more a:hover { color: var(--highlight); }
.progress__more a:focus-visible {
  outline: 2px solid var(--dark);
  outline-offset: 3px;
}
.progress__more a:hover .cat-label,
.progress__more a:focus-visible .cat-label { background-size: 100% 1px; }
@media (prefers-reduced-motion: no-preference) {
  .progress__more a:hover .cat-arrow,
  .progress__more a:focus-visible .cat-arrow { transform: translateX(4px); }
}

/* ---------------------------------------------------------------------
   Latest Posts — the ONE section with no diagonal. It keeps the upward
   bleed (that is what reveals Services' notch) but has no clip-path.
   --------------------------------------------------------------------- */
.posts__grid { --fe-rows: 18; }
@media (min-width: 768px) { .posts__grid { --fe-rows: 17; } }

.posts__heading { grid-area: 3 / 2 / 5 / 10; text-align: left; }
.posts__gallery { grid-area: 5 / 2 / 17 / 10; }
.posts__cta     { grid-area: 17 / 4 / 19 / 8; }
@media (min-width: 768px) {
  .posts__heading { grid-area: 3 / 4 / 5 / 24; }
  .posts__gallery { grid-area: 5 / 4 / 16 / 24; }
  .posts__cta     { grid-area: 16 / 11 / 18 / 17; }
}

/* Zero gap: measured slide pitch equals slide width exactly (280.5 vs
   281 @1440, 182 vs 182 @390). No hover effect is declared on the live
   thumbnails. */
.posts__gallery {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0;
}
@media (min-width: 768px) {
  .posts__gallery { grid-template-columns: repeat(4, 1fr); }
}
.posts__gallery > * { position: relative; aspect-ratio: 0.7282; }
@media (min-width: 768px) { .posts__gallery > * { aspect-ratio: 3 / 4; } }
.posts__gallery img { width: 100%; height: 100%; object-fit: cover; }

/* ---------------------------------------------------------------------
   Footer — plain block. No divider, no bleed, no z-index. No bottom
   padding, unlike the four sections above it.

   Three columns: nav links (left), the mountain brand mark (center,
   replacing the old text wordmark — redundant next to the mark), social
   links (right). Newsletter signup removed 2026-07-23 (not needed yet).
   --------------------------------------------------------------------- */
.site-footer {
  display: flex;
  align-items: center;
  padding: 56px 0;
  background: var(--white);
  color: var(--black);
}
.site-footer__grid { --fe-rows: 15; width: 100%; }

/* Absolutely positioned + inset:0 + margin:auto centers the mark within
   its grid area without the image's own size inflating the row tracks the
   nav links share (same reasoning as .hero__figure img) — lets the logo
   run much bigger than the text columns beside it without pushing gaps
   into the link list. */
.site-footer__mark { grid-area: 2 / 2 / 6 / 10; position: absolute; inset: 0; margin: auto; width: 260px; height: auto; }

.site-footer__link-1 { grid-area: 6 / 2 / 7 / 10; }
.site-footer__link-2 { grid-area: 7 / 2 / 8 / 10; }
.site-footer__link-3 { grid-area: 8 / 2 / 9 / 10; }
.site-footer__link-4 { grid-area: 9 / 2 / 10 / 10; }
.site-footer__link-5 { grid-area: 10 / 2 / 11 / 10; }
.site-footer__link-1, .site-footer__link-2, .site-footer__link-3,
.site-footer__link-4, .site-footer__link-5 { text-align: center; }

/* Service area. Deliberately dropped into rows that were already empty in
   both layouts — row 11 on mobile (between the links and the social list),
   row 7 on desktop (below the last link) — so this adds a line of text
   without changing the footer's height. Set smaller and dimmer than the
   nav links so it reads as a caption, not a sixth menu item. */
.site-footer__area {
  grid-area: 11 / 2 / 12 / 10;
  text-align: center;
  margin: 0;
  align-self: center;
  font-size: 13px;
  letter-spacing: 0.02em;
  opacity: 0.65;
}
.site-footer__link-1 a,
.site-footer__link-2 a,
.site-footer__link-3 a,
.site-footer__link-4 a,
.site-footer__link-5 a {
  transition: color 0.15s linear;
}
.site-footer__link-1 a:hover,
.site-footer__link-2 a:hover,
.site-footer__link-3 a:hover,
.site-footer__link-4 a:hover,
.site-footer__link-5 a:hover {
  color: var(--highlight);
}

.site-footer__social {
  grid-area: 12 / 2 / 15 / 10;
  justify-self: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.site-footer__social a {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  transition: color 0.15s linear;
}
.site-footer__social a:hover { color: var(--highlight); }

@media (min-width: 768px) {
  .site-footer__grid { --fe-rows: 7; }
  .site-footer__link-1 { grid-area: 2 / 2 / 3 / 10; }
  .site-footer__link-2 { grid-area: 3 / 2 / 4 / 10; }
  .site-footer__link-3 { grid-area: 4 / 2 / 5 / 10; }
  .site-footer__link-4 { grid-area: 5 / 2 / 6 / 10; }
  .site-footer__link-5 { grid-area: 6 / 2 / 7 / 10; }
  .site-footer__link-1, .site-footer__link-2, .site-footer__link-3,
  .site-footer__link-4, .site-footer__link-5 { text-align: left; }

  .site-footer__area { grid-area: 7 / 2 / 8 / 10; text-align: left; align-self: start; }

  .site-footer__mark { grid-area: 2 / 10 / 7 / 18; width: 400px; }

  .site-footer__social { grid-area: 2 / 18 / 7 / 26; justify-self: end; align-items: flex-end; }
}

/* ---------------------------------------------------------------------
   About page
   about-intro now cuts a diagonal down into Fun Facts (bg-white, bled
   upward to fill it) — same z-index/section-border mechanism used
   elsewhere on the site. Fun Facts itself stays flat into the footer,
   same as Latest Posts does on the homepage.
   --------------------------------------------------------------------- */
.about-intro { z-index: 2; padding-top: var(--header-h); }
.fun-facts { z-index: 1; }
.about-intro__grid { --fe-rows: 34; }
@media (min-width: 768px) { .about-intro__grid { --fe-rows: 15; } }

.about-intro__heading  { grid-area: 1 / 2 / 3 / 10; margin-top: clamp(16px, 4vw, 40px); font-style: italic; font-size: var(--fs-h2); }
.about-intro__sub      {
  grid-area: 3 / 2 / 6 / 10;
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: 16px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.about-intro__portrait { grid-area: 6 / 2 / 18 / 10; }
.about-intro__body     { grid-area: 19 / 2 / 35 / 10; }
@media (min-width: 768px) {
  .about-intro__heading  { grid-area: 1 / 2 / 3 / 15; }
  /* Row span shrunk from 3/2/5/15 (2 rows) to 1 row — the subtitle text
     doesn't need the second row now that it's small uppercase Lexend
     instead of a big serif h3; that empty reserved row was the actual
     gap (a negative margin on the item itself can't touch it — grid row
     sizing doesn't respond to item margins the way block-flow does). */
  .about-intro__sub      { grid-area: 3 / 2 / 4 / 15; }
  .about-intro__portrait { grid-area: 2 / 16 / 15 / 25; }
  .about-intro__body     { grid-area: 4 / 2 / 16 / 15; }
}

/* Credentials line, sitting between the subtitle and the prose. Set in the
   highlight gold and a step smaller than the subtitle so the two read as a
   pair rather than compete — subtitle says the job, this says the licences. */
.about-intro__creds {
  margin: 0 0 clamp(1.25rem, 3vw, 1.75rem);
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--highlight);
}
/* Separator as a styled span rather than a literal "·" in the sentence, so
   screen readers don't announce it and it can dim independently. */
.about-intro__creds span {
  opacity: 0.45;
  margin: 0 0.55em;
}

/* Unlike the hero images, this portrait is in normal flow on purpose: on the
   live site it is TALLER than its grid area and grows the row to fit. Pinning
   it absolutely (as the hero images need) would cap it at the area and lose
   ~230px of height. It fills the column width and takes whatever height that
   implies. */
.about-intro__portrait img {
  width: 100%;
  height: auto;
}
.about-intro__body p { margin: 0 0 1em; }
.about-intro__body p:last-child { margin-bottom: 0; }

.fun-facts {
  background: var(--white);
  color: var(--black);
  padding: clamp(3rem, 7vw, 5rem) 0;
}
.fun-facts__inner {
  max-width: 1500px;
  margin: 0 auto;
  padding: 0 var(--fe-gutter);
}
.fun-facts__heading { text-align: center; }

/* Slider: three slides (film / coffee / wedding), one visible at a time.
   All stacked in the same grid cell rather than display:none/block, so
   switching slides can crossfade instead of hard-cutting. */
.fun-facts__slider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: clamp(2rem, 5vw, 3rem) auto 0;
  max-width: 900px;
}
.fun-facts__track { display: grid; flex: 1; min-width: 0; }
.fun-facts__slide {
  grid-column: 1;
  grid-row: 1;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
.fun-facts__slide.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
.fun-facts__image {
  width: 100%;
  height: auto;
  display: block;
}
.fun-facts__text {
  max-width: 640px;
  margin: 2rem auto 0;
  display: grid;
  gap: 1rem;
  text-align: center;
}
.fun-facts__text p { margin: 0; }
.fun-facts__text a {
  border-bottom: 1px solid currentColor;
  transition: color 0.15s linear, border-color 0.15s linear;
}
.fun-facts__text a:hover { color: var(--highlight); border-color: var(--highlight); }

.fun-facts__arrow {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  border: 0;
  background: none;
  color: var(--black);
  cursor: pointer;
  opacity: 0.6;
  transition: opacity 0.15s linear;
}
.fun-facts__arrow:hover { opacity: 1; }

.fun-facts__dots {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 1.75rem;
}
.fun-facts__dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.2);
  cursor: pointer;
  transition: background-color 0.15s linear;
}
.fun-facts__dot.is-active { background: var(--highlight); }

/* ---------------------------------------------------------------------
   Contact page
   --------------------------------------------------------------------- */
/* The hero here is white, so the transparent header's white text would be
   invisible. `header-on-light` on <body> flips it. */
.header-on-light .site-header { color: var(--black); }

.contact-hero { padding-top: var(--header-h); }
.contact-hero__grid { --fe-rows: 4; }
.contact-hero__heading { grid-area: 2 / 3 / 4 / 9; text-align: center; font-style: italic; }
@media (min-width: 768px) { .contact-hero__heading { grid-area: 2 / 5 / 4 / 23; } }

/* Full-bleed horizontal strip. Images keep their own aspect ratio at a fixed
   height, exactly as the live carousel lays them out. */
.contact-strip {
  display: flex;
  overflow-x: auto;
  background: var(--dark);
  scrollbar-width: none;
}
.contact-strip::-webkit-scrollbar { display: none; }
.contact-strip img {
  height: 270px;
  width: auto;
  flex: 0 0 auto;
  object-fit: cover;
}

/* z-index + section-border/divider-down (added directly in the HTML) give
   this the same diagonal-into-footer treatment as the category and
   gallery pages; .footer-reveal on the footer bleeds white upward to
   fill the cut. padding-bottom comes from .section's own rule now
   instead of a manual bottom clamp. */
.contact-form-section {
  z-index: 2;
  color: var(--white);
  padding-top: clamp(3rem, 8vw, 5rem);
}
.contact-form-section__inner {
  max-width: 1500px;
  margin: 0 auto;
  padding: 0 var(--fe-gutter) clamp(2rem, 6vw, 4rem);
}
/* Sits above the form and shares its column so the two read as one block.
   Slightly larger than body copy and a touch dimmed — it introduces the form
   rather than competing with it. */
.contact-intro {
  max-width: 640px;
  margin: 0 auto clamp(2rem, 5vw, 3rem);
}
.contact-intro p {
  margin: 0 0 1rem;
  font-size: 17px;
  line-height: 1.65;
  opacity: 0.88;
}
.contact-intro p:last-child { margin-bottom: 0; }
.contact-intro strong { font-weight: 700; opacity: 1; }

.contact-form {
  display: grid;
  gap: 1.15rem;
  max-width: 640px;
  margin: 0 auto;
}
.contact-form .field-group {
  border: 0;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.8rem;
}
.contact-form legend {
  padding: 0;
  margin-bottom: 0.4rem;
  font-size: 14px;
  letter-spacing: 0.04em;
  opacity: 0.8;
}
.field-row { display: grid; gap: 1.2rem; grid-template-columns: 1fr; }
@media (min-width: 600px) { .field-row { grid-template-columns: 1fr 1fr; } }

.contact-form .field label {
  display: block;
  font-size: 14px;
  letter-spacing: 0.04em;
  margin-bottom: 0.5rem;
  opacity: 0.8;
}
.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form input[type="tel"],
.contact-form input[type="date"],
.contact-form select,
.contact-form textarea {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.4);
  color: var(--white);
  font-family: inherit;
  font-size: 16px;
  font-weight: 400;
  padding: 0.6rem 0;
}
.contact-form textarea { resize: vertical; min-height: 110px; }
.contact-form select option { background: var(--dark); }
.contact-form input:focus-visible,
.contact-form select:focus-visible,
.contact-form textarea:focus-visible {
  outline: 2px solid var(--white);
  outline-offset: 2px;
}
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 14px;
}
.checkbox-row label { margin: 0; opacity: 0.9; }
.contact-form__submit {
  height: 68px;
  justify-self: start;
  margin-top: 0.5rem;
}

/* Honeypot — kept out of the layout and out of the accessibility tree, but not
   display:none (some bots skip hidden fields; this stays "present"). */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
.form-status {
  margin-top: 0.5rem;
  font-size: 14px;
}
.form-status.is-error {
  color: #ff9d9d;
}

/* Accessible label that stays out of the visual design. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------
   Category pages (/fitness-athletes/, /sports-events/, /health-adventure/)
   Same dark section the topo-hero used, minus the topo art — flat
   bg-dark instead. Title + gallery grid both sit on it; the diagonal cut
   at the bottom is filled by the footer's own bled-up white background
   (.footer-reveal — shared with individual project pages below), same
   mechanism as before.
   --------------------------------------------------------------------- */
.category-hero { z-index: 2; padding-top: var(--header-h); }
.category-hero__title {
  padding: 24px 0 40px;
  text-align: center;
}
.category-hero__title h1 { margin: 0; font-size: var(--fs-h2); font-style: italic; }
/* Gelasio has no weight below 400, so the thin look borrows Lexend
   (200 fetched specifically for this) instead of trying to fake a
   lighter serif. */
.category-hero__title .amp { color: var(--highlight); font-family: "Lexend", sans-serif; font-weight: 200; font-style: normal; }
/* Shared by category.html and project.html — bleeds white upward to fill
   whichever dark section's diagonal cut sits directly above it. */
.footer-reveal { position: relative; z-index: 1; }

/* ---------------------------------------------------------------------
   Individual project pages (/portfolio/<slug>/)
   Same dark-section + diagonal-into-footer treatment as category pages.
   --------------------------------------------------------------------- */
.project-page-hero { z-index: 2; padding-top: var(--header-h); }

/* Masonry gallery — plain CSS columns rather than a JS library, matching
   the "no framework" rule for this site. Natural aspect ratios throughout
   (no cropping), packed tightly like the reference Mike sent (a Pic-Time
   client gallery + a Squarespace portrait page) rather than the old fixed
   2-up grid with an every-third-image full-bleed break. */
.project-gallery {
  column-count: 2;
  column-gap: var(--fe-gap);
  padding-bottom: clamp(3rem, 8vw, 6rem);
}
@media (min-width: 600px) { .project-gallery { column-count: 3; } }
@media (min-width: 1100px) { .project-gallery { column-count: 4; } }
.project-gallery figure {
  break-inside: avoid;
  margin: 0 0 var(--fe-gap);
}
.project-gallery img { width: 100%; height: auto; display: block; cursor: pointer; }

/* Lightbox — plain CSS + vanilla JS (see js/main.js), matching the site's
   no-framework rule. One fixed overlay, reused for every photo clicked. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.92);
  padding: clamp(16px, 5vw, 64px);
}
.lightbox[hidden] { display: none; }
.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.lightbox__close,
.lightbox__prev,
.lightbox__next {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  border: 0;
  background: none;
  color: var(--white);
  cursor: pointer;
  opacity: 0.8;
  transition: opacity 0.15s linear;
}
.lightbox__close:hover,
.lightbox__prev:hover,
.lightbox__next:hover {
  opacity: 1;
}
.lightbox__close { top: clamp(8px, 3vw, 24px); right: clamp(8px, 3vw, 24px); }
.lightbox__prev { left: clamp(4px, 2vw, 16px); top: 50%; transform: translateY(-50%); }
.lightbox__next { right: clamp(4px, 2vw, 16px); top: 50%; transform: translateY(-50%); }
body.lightbox-open { overflow: hidden; }

/* Gallery titles in Lexend rather than the default Gelasio serif, to read
   more like a label than a headline. */
.category-page .project-card h3 {
  font-family: "Lexend", sans-serif;
  font-weight: 500;
  font-size: 17px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* The summary shown here (see build.mjs) reads better upright than in the
   .project-eyebrow base style's italic. */
.category-page .project-card .project-eyebrow { font-style: normal; }
