/* ==========================================================================
   New Hope Collaborative — leaded glass
   Every colour below is lifted from the lion mark's own vectors.
   ========================================================================== */

/* --- Faces ---------------------------------------------------------------- */

@font-face {
  font-family: "Josefin Sans";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/josefin-sans-normal-300_700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Josefin Sans";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../fonts/josefin-sans-normal-300_700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Alegreya";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/alegreya-normal-400_700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Alegreya";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/alegreya-normal-400_700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Alegreya";
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url("../fonts/alegreya-italic-400_500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Alegreya";
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url("../fonts/alegreya-italic-400_500-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- Tokens --------------------------------------------------------------- */

:root {
  /* Glass, straight off the lion */
  --amber:      #fa9c10;
  --orange:     #f25014;
  --vermilion:  #f0431c;
  --crimson:    #c50c00;
  --plum:       #812d50;
  --teal:       #008fa9;
  --deep-teal:  #005268;
  --blue:       #044680;

  /* Not in the lion. Ekklesia needed its own band because Advocacy already
     holds the blue end, and two blues read as one organization. Cut to the
     same register as the rest of the deep glass — H140, S62%, L18%, 9.7:1 on
     --lit — so it sits with them rather than beside them. */
  --deep-green: #114924;

  /* Also not in the lion. Own Your Dream's mark is a full spectrum -- a tree
     of teal, pink, orange, amber and green leaves -- and it ran on #7d2b08,
     --orange mixed toward --lead. At H18 that band sat inside the logo's own
     warm range, so the leaves and the orange E in DREAM sank into it and the
     came-glass plinth read as a hole punched in the band. Violet is the only
     hue the other five partners leave free: 58 degrees off Advocacy's --blue
     and 69 off Bridge's --plum, far enough that no two partner pages read as
     the same organization.

     Cut to 9.6:1 on --lit, matching --deep-green at the dark end of the range
     the bands span. It cannot go darker and stay in that range: violet is an
     intrinsically dark hue, so matching the others' LIGHTNESS instead would
     have landed it at 12-15:1 and made this one band visibly heavier than its
     five siblings. Contrast is the constraint that wins here. */
  --violet:     #52288a;

  /* Came — the lead between panes */
  --lead:       #0d1418;
  --lead-soft:  #1d2933;

  /* Ground and lit glass. Three depths of the same water: --ground-lo for
     masthead and footer, --ground for the page, --ground-hi pulled toward the
     lion's own blue (#044680) to lift a section off the one above it. */
  --ground:     #0a2337;
  --ground-lo:  #071a29;
  --ground-hi:  #103252;
  --lit:        #fcf6ea;
  --lit-edge:   #ecdfc8;

  /* Ink */
  --ink:        #14212c;
  --ink-soft:   #4a5a68;
  --on-dark:    #f4efe6;
  --on-dark-soft: #a9bccb;

  /* Type */
  --display: "Josefin Sans", ui-sans-serif, system-ui, sans-serif;
  --body: "Alegreya", ui-serif, Georgia, serif;

  /* Rhythm */
  --gutter: clamp(1.25rem, 5vw, 4rem);
  --measure: 68ch;
  --lead-w: 3px;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* --- Reset ---------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--on-dark);
  font-family: var(--body);
  font-size: clamp(1.0625rem, 0.4vw + 1rem, 1.1875rem);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin: 0;
}

p { margin: 0; text-wrap: pretty; }

a { color: inherit; }

:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--amber); color: var(--lead); }

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--amber);
  color: var(--lead);
  padding: 0.75rem 1.25rem;
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 0.8125rem;
}
.skip:focus { left: 0; }

.vh {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Shell ---------------------------------------------------------------- */

.wrap {
  width: min(100% - (var(--gutter) * 2), 78rem);
  margin-inline: auto;
}
.wrap--narrow { width: min(100% - (var(--gutter) * 2), 46rem); }

/* --- Masthead ------------------------------------------------------------- */

.masthead {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--ground-lo);
  border-bottom: var(--lead-w) solid var(--lead);
}

.masthead__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding-block: 0.75rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  text-decoration: none;
  flex-shrink: 0;
}
.brand img { width: 3rem; height: 3rem; }
.brand__name {
  font-family: var(--display);
  font-weight: 600;
  font-size: 0.9375rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  line-height: 1.15;
  color: var(--on-dark);
}
.brand__name span {
  display: block;
  font-weight: 300;
  letter-spacing: 0.24em;
  font-size: 0.75rem;
  color: var(--amber);
}

.nav { display: flex; flex-wrap: wrap; gap: 0.25rem 0.5rem; align-items: center; }

.nav a {
  font-family: var(--display);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--on-dark-soft);
  padding: 0.5rem 0.6rem;
  transition: color 0.2s var(--ease), background-color 0.2s var(--ease);
}
.nav a:hover { color: var(--lead); background: var(--amber); }
.nav a[aria-current="page"] { color: var(--amber); }
.nav a[aria-current="page"]:hover { color: var(--lead); }

.nav .nav__give {
  background: var(--crimson);   /* 5.7:1 with --lit; vermilion only reached 3.5 */
  color: var(--lit);
  font-weight: 600;
}
.nav .nav__give:hover { background: var(--amber); color: var(--lead); }

/* --- The window: the signature ------------------------------------------- */
/* Panes of glass held in lead. Sizes differ on purpose — five organisations,
   not five identical cards. */

.window {
  display: grid;
  gap: var(--lead-w);
  background: var(--lead);
  border: var(--lead-w) solid var(--lead);
  grid-template-columns: repeat(6, 1fr);
}

.pane {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.875rem;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  min-height: 15rem;
  text-decoration: none;
  color: var(--lit);
  overflow: hidden;
  isolation: isolate;
  transition: filter 0.45s var(--ease);
}

/* Light through glass, on hover and on keyboard focus alike */
.pane::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(120% 90% at 30% 110%, rgba(255, 255, 255, 0.34), transparent 68%);
  opacity: 0;
  transition: opacity 0.45s var(--ease);
}
.pane:hover::after,
.pane:focus-visible::after { opacity: 1; }
.pane:hover { filter: saturate(1.15) brightness(1.06); }

.pane__name {
  font-family: var(--display);
  font-size: clamp(1.375rem, 1.6vw + 0.9rem, 2rem);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.06;
}
.pane__role {
  font-family: var(--display);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}
.pane__line { font-size: 0.9375rem; line-height: 1.5; max-width: 30ch; }

/* Pane fills are chosen for contrast as well as colour: every one of these
   clears 4.5:1 against the text it carries. The lion's brighter oranges take
   dark ink; its deeper glass takes light. */
.pane--amber     { background: var(--amber);     color: var(--lead); }  /* 11.2:1 */
.pane--orange    { background: var(--orange);    color: var(--lead); }  /*  5.2:1 */
.pane--crimson   { background: var(--crimson);   }                      /*  5.7:1 */
.pane--plum      { background: var(--plum);      }                      /*  8.0:1 */
.pane--deep-teal { background: var(--deep-teal); }                      /*  8.1:1 */
.pane--blue      { background: var(--blue);      }                      /*  9.3:1 */

/* Deliberately uneven — the window is leaded, not gridded */
.pane--wide { grid-column: span 4; }
.pane--half { grid-column: span 3; }
.pane--third { grid-column: span 2; }
.pane--tall { grid-row: span 2; }

/* Sunrise: the panes light in sequence, once, on load */
@media (prefers-reduced-motion: no-preference) {
  .window--sunrise .pane {
    animation: pane-light 0.9s var(--ease) backwards;
  }
  .window--sunrise .pane:nth-child(1) { animation-delay: 0.05s; }
  .window--sunrise .pane:nth-child(2) { animation-delay: 0.14s; }
  .window--sunrise .pane:nth-child(3) { animation-delay: 0.23s; }
  .window--sunrise .pane:nth-child(4) { animation-delay: 0.32s; }
  .window--sunrise .pane:nth-child(5) { animation-delay: 0.41s; }
  .window--sunrise .pane:nth-child(6) { animation-delay: 0.50s; }
}

@keyframes pane-light {
  from { filter: brightness(0.42) saturate(0.5); }
  to   { filter: none; }
}

/* --- Partner panes -------------------------------------------------------- */
/* The partner treatment on index and Partners. A pane of the organization's own
   glass carrying its mark, over a texture: a photograph where one exists, the
   stripe where one does not.

   This replaced a photograph-first tile. Half the partners have no photograph,
   and the ones that do are documentary rather than invitational, so at the
   larger size six partners force, the row read as three pictures and three
   stand-ins. Demoting the image to texture under the glass makes a missing
   photograph a non-event: an unphotographed pane is finished, not empty. It
   also gives the mark somewhere to live, and the mark is what tells six
   organizations apart -- a job the colour alone was doing badly, since the row
   is six links differing only in hue and a name in one typeface.

   Leading rides on each pane, not behind the grid, so a partly filled last row
   ends on page ground instead of a dead black cell. */

/* Flex, not grid, but with the column count pinned: the number of partners
   will change, and a free-flowing row picks its own count from the available
   width. At six that produced four across and two stranded beside a gap. The
   count is now stated rather than inferred -- three across on a desktop, two
   on a tablet, one on a phone -- so six lands as two full rows.
   Three is also the divisor that degrades best: it is right for three, six and
   nine, and any other count leaves a short last row rather than a broken one. */
.tiles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tile-gap);
  --tile-gap: 0.75rem;
  --tile-cols: 1;
}
@media (min-width: 40rem)  { .tiles { --tile-cols: 2; } }
@media (min-width: 62rem)  { .tiles { --tile-cols: 3; } }

.tile {
  position: relative;
  /* Basis and ceiling are the same value -- one column of the pinned grid.
     Equal, because a growing tile is what stranded the last row: with the
     ceiling above the basis, a lone leftover widens to fill and hands whoever
     sorts last an accidental promotion. A short row now ends short. */
  --tile-w: calc((100% - (var(--tile-gap) * (var(--tile-cols) - 1))) / var(--tile-cols));
  flex: 1 1 var(--tile-w);
  max-width: var(--tile-w);
  display: flex;
  flex-direction: column;
  /* Mark at the top, words at the foot. The middle is left to the glass. */
  justify-content: space-between;
  gap: 1.25rem;
  min-height: 18rem;
  --pad: clamp(1.25rem, 2.5vw, 1.875rem);
  padding: var(--pad);
  border: var(--lead-w) solid var(--lead);
  background: var(--ground-lo);
  text-decoration: none;
  color: var(--lit);
  overflow: hidden;
  isolation: isolate;
}
/* A pinned three-column pane is wider than the old free-flowing one, so the
   floor rises with it and the pane keeps its proportion instead of flattening
   into a letterbox. */
@media (min-width: 62rem) { .tile { min-height: 20.5rem; } }

/* Texture, not subject. Greyscaled so the wash above is the only hue in the
   pane, and darkened because the wash is translucent: a bright sky coming
   through at full strength is what would put the role label under 4.5:1. */
.tile__media {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--ground-lo);
}
.tile__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Favour the lower half -- that is where people are in a room photograph,
     and a centred crop tends to deliver ceiling. */
  object-position: center 62%;
  filter: grayscale(1) brightness(0.5);
  transition: transform 0.6s var(--ease), filter 0.45s var(--ease);
}
/* Hover brings the photograph up through the glass: some colour back, some
   brightness back, and the wash thinned to let it read. The words are safe
   from all of it -- .tile__text is an opaque pane above the wash, not a scrim
   over it -- so this can be generous without costing contrast. Measured at the
   hover end of the range against the brightest pixel any of the photographs
   contains, the role label still clears 4.8:1. */
.tile:hover .tile__media img,
.tile:focus-visible .tile__media img {
  transform: scale(1.045);
  filter: grayscale(0.3) brightness(0.82);
}

/* The stand-in where there is no photograph. It no longer says "Photo": under
   this treatment a pane without one is a finished pane, not a placeholder, so
   announcing the gap on the page would be announcing a non-problem. The
   outstanding photography is tracked in NOTES.md. */
.tile__media--empty {
  background:
    repeating-linear-gradient(45deg,
      rgba(252, 246, 234, 0.08) 0 14px,
      transparent 14px 28px),
    var(--ground-lo);
}

/* The glass itself. Every pane is its own colour mixed into --ground-lo rather
   than the raw hue, which puts all six in one register: --amber and --blue are
   four stops of lightness apart in the palette and would otherwise need
   different text treatments. The same formula the partner-page bands use, and
   the reason light text on a pane is a constant rather than a function of the
   photograph underneath. */
.tile__wash {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--ground-lo);
  background: color-mix(in oklab, var(--glass) 50%, var(--ground-lo));
  opacity: 0.86;
  transition: opacity 0.3s var(--ease);
}
/* The pane lights on hover -- the same idea as the load animation, and unlike
   the photo scale it works on all six, not only the ones with a photograph. */
.tile:hover .tile__wash,
.tile:focus-visible .tile__wash { opacity: 0.64; }

/* The mark, on the same three surfaces the partner pages use, for the same
   reasons -- a mark drawn for a light ground gets lit glass, one drawn for
   dark gets a panel or came glass. Keeping the two treatments identical means
   a visitor meets the same object on the list and on the page. No came: the
   plinth sits on glass, and a frame around a frame reads as clutter at this
   size. */
.tile__mark {
  position: relative;
  z-index: 3;
  align-self: flex-start;
  width: clamp(7.5rem, 50%, 10.5rem);
  aspect-ratio: 320 / 220;
  /* flex: none + min-height: 0 are both load-bearing. As a flex item in a
     column, this box's automatic minimum size is its content height, and that
     beats aspect-ratio -- which is the other half of why the plinths came out
     unequal. */
  flex: none;
  min-height: 0;
  display: grid;
  /* Load-bearing, and easy to leave out: without an explicit row the grid
     track is auto, so a mark with a squarer ratio than the box sizes the row
     from its own intrinsic height and overrides aspect-ratio. That is how the
     six plinths ended up 119, 109, 95, 95, 95 and 95 pixels tall. The
     partner-page plinths carry the same line for the same reason. */
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  padding: 0.7rem 0.85rem;
}
.tile__mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.tile__mark--panel { background: var(--ground); }              /* drawn for dark */
.tile__mark--shade { background: rgba(13, 20, 24, 0.85); }     /* came glass */
/* Lit glass, but thinner than the 0.85 the partner pages use. At that opacity
   it is effectively white, and one white plinth in a row of five dark ones
   pulls the eye to whichever partner happens to need it -- the accidental
   promotion this layout works to avoid everywhere else. At 0.72 it takes
   enough of its own glass to belong to the row, and the navy in the Extended
   Hands mark still reads 5.9:1 against it, well clear of the 2.4:1 it manages
   on came. */
.tile__mark--wash  { background: rgba(252, 246, 234, 0.72); }  /* drawn for light */

/* The words sit on their own pane, not under a gradient. This is the
   .hero__copy::before decision again, and it is here for the same reason: a
   gradient over the whole pane has to choose a strength for one position, and
   the role label sits at the TOP of the text block rather than the bottom. On
   --amber, the brightest glass, that put it in the weak part of the ramp and
   washed it out. A flat panel makes contrast a constant of the panel rather
   than a function of where the block happens to end, and it holds however many
   lines the copy runs to.

   It also spends one less gradient. Bleeds to the pane edges on the tile's own
   padding, with came along the top, so it reads as a second pane of glass
   rather than a box floating inside the first. */
.tile__text {
  position: relative;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0 calc(var(--pad) * -1) calc(var(--pad) * -1);
  padding: var(--pad);
  background: rgba(7, 26, 41, 0.86);
  border-top: var(--lead-w) solid var(--lead);
  /* A floor, not a fixed height. The foot grows with its copy and the glass
     above it shrinks to match -- so "Advocacy to Alleviate Homelessness",
     the one name long enough to wrap, was taking 27px of its own glass and
     leaving its mark in a shallower pane than the other five. The floor holds
     every foot at the two-line-name height, so the glass band is the same
     depth across the row whatever the copy does. Longer copy still grows the
     foot; this only stops the short ones riding up. Off below the
     three-column breakpoint, where the panes are wide and nothing wraps.

     12rem is the Advocacy foot measured: a two-line name against two lines of
     copy, which is why that one line was shortened rather than left to run to
     three. The other five sit 5px under the floor instead of 27px under it, so
     the slack the floor adds is invisible. Re-measure if the copy changes. */
  min-height: 12rem;
}
@media (max-width: 62rem) { .tile__text { min-height: 0; } }
.tile__role {
  font-family: var(--display);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
}
.tile__name {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.25rem, 1.1vw + 0.95rem, 1.5rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.tile__line {
  font-size: 0.9375rem;
  line-height: 1.45;
  /* One step up from the old #cddae4. The scrim now sits on a translucent
     colour field rather than on near-black, so the floor moved and this had to
     move with it to stay over 4.5:1 on --amber, the brightest glass. */
  color: #d8e3ec;
  max-width: 34ch;
}

@media (prefers-reduced-motion: no-preference) {
  .tiles--sunrise .tile { animation: tile-light 0.8s var(--ease) backwards; }
  .tiles--sunrise .tile:nth-child(1) { animation-delay: 0.05s; }
  .tiles--sunrise .tile:nth-child(2) { animation-delay: 0.13s; }
  .tiles--sunrise .tile:nth-child(3) { animation-delay: 0.21s; }
  .tiles--sunrise .tile:nth-child(4) { animation-delay: 0.29s; }
  .tiles--sunrise .tile:nth-child(5) { animation-delay: 0.37s; }
  .tiles--sunrise .tile:nth-child(6) { animation-delay: 0.45s; }
  .tiles--sunrise .tile:nth-child(7) { animation-delay: 0.53s; }
  .tiles--sunrise .tile:nth-child(8) { animation-delay: 0.61s; }
}
@keyframes tile-light {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* --- Ways in -------------------------------------------------------------- */
/* Wayfinding, not organizations. The circle is a mark rather than a container,
   so copy keeps full width and the row stays lighter than the partner tiles
   above it — three links should not carry the same visual authority as five
   organizations. Panels sit at page ground on the lifted band, so the frame
   comes from the existing palette rather than a new colour. */

.ways {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
}

.ways__item {
  display: flex;
  gap: 1.125rem;
  align-items: flex-start;
  padding: clamp(1.25rem, 2.2vw, 1.75rem);
  background: var(--ground);
  text-decoration: none;
  color: var(--on-dark);
  transition: background-color 0.3s var(--ease);
}
.ways__item:hover { background: var(--ground-lo); }

/* Grows with the viewport: ~3.25rem on a phone, ~4.25rem on a wide screen.
   The glyph tracks it at roughly 42% so the mark keeps its proportions. */
.ways__dot {
  flex: 0 0 auto;
  width: clamp(3.25rem, 2.2vw + 2.4rem, 4.25rem);
  height: clamp(3.25rem, 2.2vw + 2.4rem, 4.25rem);
  border-radius: 50%;
  display: grid;
  place-items: center;
  transition: transform 0.35s var(--ease);
}
.ways__dot svg {
  width: clamp(1.375rem, 0.9vw + 1rem, 1.8125rem);
  height: clamp(1.375rem, 0.9vw + 1rem, 1.8125rem);
}
.ways__item:hover .ways__dot,
.ways__item:focus-visible .ways__dot { transform: scale(1.12); }

.ways__name {
  display: block;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.1875rem;
  line-height: 1.15;
  margin-bottom: 0.4rem;
}
.ways__item:hover .ways__name { color: var(--amber); }
.ways__line {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--on-dark-soft);
}

/* --- Photo band ----------------------------------------------------------- */

.photo-band { margin: 0; border-block: var(--lead-w) solid var(--lead); }
.photo-band img {
  display: block;
  width: 100%;
  height: clamp(15rem, 42vw, 30rem);
  object-fit: cover;
}
.photo-band figcaption {
  background: var(--ground-lo);
  color: var(--on-dark-soft);
  padding: 0.875rem 0;
  font-size: 0.875rem;
}

/* --- Figure at the text measure ------------------------------------------- */
/* A photograph inside the prose rather than between the bands. .photo-band
   crops to a fixed height with object-fit: cover, which on a desktop turns a
   4:3 frame into roughly 2.4:1 and throws away the top and bottom of the
   picture -- fine for the campus landscapes it was built for, wrong for a
   photograph of people. At the measure the picture keeps its own proportions,
   and it sits with the paragraph that introduces it instead of interrupting
   the page. Came, because every other image on this site has an edge. */
.figure {
  /* margin-inline: 0 is load-bearing -- a bare <figure> carries a UA default of
     40px each side, which indents it out of alignment with the prose and takes
     80px off its width. */
  margin-inline: 0;
  max-width: var(--measure);
}
.prose > .figure { margin-top: 2.25rem; }
.figure img {
  display: block;
  width: 100%;
  height: auto;
  /* One ratio across the three partner photographs. They arrive 4:3, 6:5 and
     5:4, and a column of pictures that are each a slightly different shape
     reads as an accident rather than a set. Cropped here rather than
     re-encoded: the loss is 10% off Loaves and Fishes and 6% off Bridge, both
     off the top and bottom where there is nothing, and re-encoding would mean
     new filenames under the asset-cache rule for no gain. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
  /* No edge at all. Three treatments were built and looked at: the panes' 3px
     came, which framed the photograph rather than edging it and read as a
     picture frame at this size; a 1px hairline; and nothing. Curtis chose
     nothing, having seen the hairline in place.

     The known cost, so nobody rediscovers it as a bug: a photograph that is
     pale at its margins loses its edge against --lit. The Loaves and Fishes
     interior is cream walls and is the one in the set that does this. The
     fallback, if a future photograph does it worse, is `border: 1px solid
     var(--lead)` here -- not came at 3px.

     A drop shadow was the fourth candidate and is deliberately not here. Not
     because shadows are dated -- they are not -- but because this design
     expresses depth through colour value and came, and nothing else on the
     site claims to float. One shadow would be the only element speaking a
     different language. See the opening of this file. */
}
.figure figcaption {
  margin-top: 0.75rem;
  font-size: 0.9375rem;
  line-height: 1.45;
  color: var(--ink-soft);      /* 5.8:1 on --lit */
}

/* A named person with a face. Square and hard-edged like everything else —
   a circular crop would be the one rounded corner on the site. The photo is
   flex: none so a long name never squeezes it out of square. */
.person { display: flex; gap: 1.25rem; align-items: flex-start; margin-top: 1.5rem; }
.person__photo {
  flex: none;
  display: block;
  width: 9.8rem;
  height: 12.25rem;     /* 4:5 — a portrait crop, not a square avatar */
  object-fit: cover;
  border: var(--lead-w) solid var(--lead);
}
/* The email addresses are long enough to push past a phone's edge once the
   photo takes its share of the row. Let them break rather than overflow. */
.person p { margin: 0; overflow-wrap: anywhere; }

@media (max-width: 30rem) {
  .person { gap: 1rem; }
  .person__photo { width: 7.7rem; height: 9.625rem; }
}

/* --- Sections ------------------------------------------------------------- */

.band { padding-block: clamp(3.5rem, 8vw, 7rem); }
.band--lit { background: var(--lit); color: var(--ink); }
.band--lit h1, .band--lit h2, .band--lit h3 { color: var(--lead); }
.band--deep { background: var(--ground-lo); }
.band--blue { background: var(--ground-hi); }
.band--tight { padding-block: clamp(2.5rem, 5vw, 4rem); }

/* Any two adjacent bands of different depth get the leading between them. */
.band--blue + .band:not(.band--blue),
.band:not(.band--blue) + .band--blue { border-top: var(--lead-w) solid var(--lead); }

.band + .band--lit,
.band--lit + .band { border-top: var(--lead-w) solid var(--lead); }

.section-head { max-width: 34ch; margin-bottom: clamp(2rem, 4vw, 3rem); }
.section-head h2 {
  font-size: clamp(1.875rem, 3.2vw + 1rem, 3.25rem);
  font-weight: 300;
}
.section-head h2 strong { font-weight: 600; }
.section-head p {
  margin-top: 1.125rem;
  max-width: 46ch;
  color: var(--on-dark-soft);
  font-size: 1.0625rem;
}
.band--lit .section-head p { color: var(--ink-soft); }

.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 1.25rem; }
.prose > * + h2 { margin-top: 3rem; }
.prose > * + h3 { margin-top: 2.5rem; }
.prose h2 { font-size: clamp(1.5rem, 2vw + 1rem, 2.125rem); font-weight: 600; }
.prose h3 { font-size: clamp(1.1875rem, 1vw + 0.9rem, 1.4375rem); font-weight: 600; }
.prose a { color: inherit; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
.prose ul { margin: 0; padding-left: 1.25rem; }
.prose li + li { margin-top: 0.5rem; }

.lede {
  font-size: clamp(1.25rem, 1.4vw + 0.95rem, 1.625rem);
  line-height: 1.42;
}

/* --- Buttons -------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.9rem 1.5rem;
  font-family: var(--display);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  border: var(--lead-w) solid var(--lead);
  background: var(--amber);
  color: var(--lead);
  transition: transform 0.25s var(--ease), background-color 0.25s var(--ease);
}
.btn:hover { background: var(--lit); transform: translateY(-2px); }
.btn--quiet { background: transparent; color: var(--on-dark); border-color: var(--on-dark-soft); }
.btn--quiet:hover { background: var(--on-dark); color: var(--lead); border-color: var(--on-dark); }
.band--lit .btn--quiet { color: var(--ink); border-color: var(--ink-soft); }
.band--lit .btn--quiet:hover { background: var(--lead); color: var(--lit); }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.875rem; margin-top: 2rem; }

/* --- Hero ----------------------------------------------------------------- */

.hero {
  position: relative;
  background: var(--ground);
  border-bottom: var(--lead-w) solid var(--lead);
  overflow: hidden;
}

/* Video hero. The poster is a CSS background on the section rather than the
   <video> poster attribute, so that hiding the video under reduced-motion
   leaves a still image behind instead of a black rectangle — and so the still
   is what shows if the file is missing, slow, or blocked. */
.hero--video {
  background-image: var(--hero-still);
  background-size: cover;
  background-position: center 55%;
}
.hero__video {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 55%;
}
/* No scrim. The copy carries its own pane (below), so the footage runs
   unmasked from edge to edge. This replaced a four-stop horizontal gradient
   plus a three-stop vertical one, tuned against the brightest frame of the
   video and re-tuned twice. That approach was always fragile: it made text
   contrast a function of what the footage happened to be doing, so every
   change to the video was a silent contrast change too. An opaque pane makes
   the ratio a constant. If a scrim ever comes back, the measurement method is
   in the git history at ac59b54. */

/* The copy sits on its own pane, and the footage runs full width behind it.
   A full-width scrim had to choose between the video and the text everywhere
   at once; a pane only spends contrast where the words actually are, so the
   frame stays whole and reaches all four edges of the window.

   Semi-opaque on purpose, at the most transparent value that is actually
   safe. Measure the right statistic here: the average brightness of the
   region is misleading, because what breaks a line of text is the brightest
   *patch* that can pass behind it. Sampled in blocks, this footage puts fully
   blown highlights (Y=255 -- sky through the windows, the chandelier) inside
   the pane area in more than 5% of frames, against a region average of only
   173. Against pure white behind the text, 0.80 holds the lede at 4.84:1 and
   the amber at 4.43:1. Below about 0.78 the lede fails outright.

   So 0.80 is a floor, not a preference -- and it is a hard one, since Y=255
   is the worst a pixel can be. --ground-lo ties it to the masthead; the 3px
   lead border is the same came that divides every other pane on the site.

   Contained, not bled: video to the left of it is the point. */
.hero--video .hero__copy { position: relative; }
.hero--video .hero__copy > * { position: relative; z-index: 1; }
.hero--video .hero__copy::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: calc(-1 * clamp(1.75rem, 3.5vw, 2.75rem));
  bottom: calc(-1 * clamp(1.75rem, 3.5vw, 2.75rem));
  left: calc(-1 * clamp(1.5rem, 3vw, 2.5rem));
  right: calc(-1 * clamp(1rem, 2vw, 1.75rem));
  background: rgba(7, 26, 41, 0.80);
  border: var(--lead-w) solid var(--lead);
}

.hero--video .hero__inner { position: relative; z-index: 2; }

@media (prefers-reduced-motion: reduce) {
  .hero__video { display: none; }
}

/* The video plays on phones too. It is 3.8MB, which is real money on a metered
   connection, so anyone who has told their browser to conserve data gets the
   still instead -- the same fallback reduced-motion users get. */
@media (prefers-reduced-data: reduce) {
  .hero__video { display: none; }
}

/* Stacked and narrow: the same pane, pulled in tight. The gutter is already
   small here, so the desktop insets would leave the border almost touching
   the screen edge. */
@media (max-width: 48rem) {
  /* Narrower than the column so a strip of footage runs down both sides, and
     tighter top and bottom so more shows above and below. The horizontal give
     is small by necessity -- the pane cannot be narrower than readable text on
     a 375px screen, so most of what is gained here is vertical. */
  .hero--video .hero__copy { max-width: 19.5rem; margin-inline: auto; }
  .hero--video .hero__copy::before {
    top: -1.25rem;
    bottom: -1.25rem;
    left: -1rem;
    right: -1rem;
  }
}
.hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: center;
  padding-block: clamp(3.5rem, 9vw, 7.5rem);
}
.hero h1 {
  font-size: clamp(2.5rem, 6.2vw + 0.5rem, 5.5rem);
  font-weight: 300;
  letter-spacing: -0.035em;
}
.hero h1 strong { font-weight: 600; color: var(--amber); }
.hero__lede {
  margin-top: 1.75rem;
  max-width: 42ch;
  font-size: clamp(1.125rem, 0.9vw + 0.95rem, 1.4375rem);
  line-height: 1.48;
  color: var(--on-dark-soft);
}
.hero__mark { justify-self: center; width: min(100%, 22rem); }
.hero__mark img { width: 100%; }

/* --- Address / detail lists ---------------------------------------------- */

.facts {
  display: grid;
  gap: 0;
  border: var(--lead-w) solid var(--lead);
  background: var(--lead);
  /* Wide enough that the address does not break mid-line once the circle takes
     its indent. Four facts land as a 2×2 on a laptop. The min() is load-bearing:
     a bare 26rem minimum is wider than a phone, so the track refuses to shrink
     and the whole row hangs off the right edge. */
  grid-template-columns: repeat(auto-fit, minmax(min(26rem, 100%), 1fr));
  row-gap: var(--lead-w);
  column-gap: var(--lead-w);
}
/* Five facts leave the sixth cell empty, and because the grid paints --lead
   behind its cells that empty cell is a hole in the box rather than nothing.
   An odd last fact takes the whole row instead. The 26rem minimum caps the
   grid at two tracks inside a 78rem wrap, so :nth-child(odd) is the same
   question as "alone on its row"; revisit if that minimum ever drops. */
.facts > .fact:last-child:nth-child(odd) { grid-column: 1 / -1; }
.fact {
  --mark: clamp(2.75rem, 1.6vw + 2.2rem, 3.5rem);
  --mark-gap: 0.875rem;
  background: var(--lit);
  color: var(--ink);
  padding: clamp(1.25rem, 2.5vw, 1.875rem);
}
.fact dt {
  display: flex;
  align-items: center;
  gap: var(--mark-gap);
  font-family: var(--display);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.75rem;
}
/* The mark lives inside the <dt> so the <dl> keeps a valid dt/dd structure. */
.fact__mark {
  flex: 0 0 auto;
  width: var(--mark);
  height: var(--mark);
  border-radius: 50%;
  display: grid;
  place-items: center;
}
.fact__mark svg { width: 45%; height: 45%; }
/* Indent the value to sit under the label rather than under the circle. */
.fact dd {
  margin: 0 0 0 calc(var(--mark) + var(--mark-gap));
  font-size: 1.0625rem;
  line-height: 1.45;
}

@media (max-width: 30rem) {
  .fact dd { margin-left: 0; }
}
.fact dd a { color: var(--blue); }

/* --- Partner masthead (individual partner pages) ------------------------- */

.partner-head {
  border-bottom: var(--lead-w) solid var(--lead);
  padding-block: clamp(2.5rem, 6vw, 4.5rem);
}
.partner-head__inner {
  display: grid;
  grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
  gap: clamp(1.75rem, 4vw, 3.5rem);
  align-items: center;
}
.partner-head__logo {
  background: var(--lit);
  border: var(--lead-w) solid var(--lead);
  padding: 1.25rem;
  display: grid;
  place-items: center;
  min-height: 9rem;
}
/* Cap the height so logos of different proportions sit at one weight, but let
   width fill the plate -- a near-square mark hits the height cap while still
   leaving the plate half empty otherwise. */
.partner-head__logo img { max-height: 9.5rem; max-width: 100%; width: auto; }

/* Three surfaces, one box. A mark that carries its own background -- drawn for
   dark, or resting on translucent glass -- wants a surface rather than a
   frame, so all three drop the came and share the wider column, the padding,
   and a mark that fills its box. Only the fill differs. Bordered plates keep
   the narrower 16rem box, because a frame needs to sit tighter to the art.
   Padding matches .ways__item so a panel reads as the same object. */
.partner-head__logo--panel,
.partner-head__logo--wash,
.partner-head__logo--shade {
  border: 0;
  min-height: 0;
  padding: clamp(1.25rem, 2.2vw, 1.75rem);
  aspect-ratio: 320 / 220;
  grid-template-rows: minmax(0, 1fr);
}
/* One box, whatever the mark. 320x220, chosen for height rather than derived from any one mark; every
   partner letterboxes into it rather than setting its own height, so a visitor
   clicking through five partner pages sees the header hold still. Set on the
   box, not the image, so Extended Hands' text name-mark matches too. */
.partner-head__logo--panel img,
.partner-head__logo--wash img,
.partner-head__logo--shade img {
  max-height: none;
  width: 100%;
  height: 100%;
  min-height: 0;
  object-fit: contain;
}
.partner-head__inner:has(.partner-head__logo--panel),
.partner-head__inner:has(.partner-head__logo--wash),
.partner-head__inner:has(.partner-head__logo--shade) {
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
}

.partner-head__logo--panel { background: var(--ground); }   /* mark drawn for dark */
.partner-head__logo--wash  { background: rgba(252, 246, 234, 0.85); }  /* lit glass */
.partner-head__logo--shade { background: rgba(13, 20, 24, 0.85); }     /* came glass */

.partner-head h1 {
  font-size: clamp(2rem, 4.4vw + 0.5rem, 3.75rem);
  font-weight: 600;
}
.partner-head__role {
  font-family: var(--display);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 0.875rem;
}
.partner-head__line {
  margin-top: 1.125rem;
  max-width: 50ch;
  color: var(--on-dark-soft);
  font-size: 1.125rem;
}

/* --- Priorities (About page) --------------------------------------------- */

.priorities { display: grid; gap: var(--lead-w); background: var(--lead); border: var(--lead-w) solid var(--lead); }
.priority {
  background: var(--lit);
  color: var(--ink);
  padding: clamp(1.5rem, 3vw, 2.25rem);
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  align-items: start;
}
.priority h3 {
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--lead);
}
.priority p { font-size: 1.0625rem; max-width: 68ch; }
/* Was 2.25 × 0.375rem and disappeared against the panel. A colour callback has
   to be seen to be a callback. */
.priority__mark {
  width: 4rem;
  height: 0.75rem;
  margin-bottom: 1rem;
  display: block;
}

/* --- Placeholder flag ----------------------------------------------------- */
/* Anything wearing this is waiting on real material from the campus. */

.todo {
  border: 2px dashed var(--plum);
  background: rgba(129, 45, 80, 0.07);
  padding: 1rem 1.25rem;
  font-family: var(--display);
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  color: var(--plum);
  margin-top: 1.5rem;
}
.todo strong {
  display: block;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.6875rem;
  margin-bottom: 0.3rem;
}
.band--deep .todo, .band:not(.band--lit) .todo {
  color: #e6b8cd;
  border-color: #b06489;
  background: rgba(176, 100, 137, 0.12);
}

/* A lit panel inside a dark band still reads as lit. Without this the flag
   inherits the dark-band treatment and goes pink-on-pink. */
.band .priority .todo, .band .fact .todo, .band--lit .todo {
  color: var(--plum);
  border-color: var(--plum);
  background: rgba(129, 45, 80, 0.07);
}

.ph-image {
  background:
    repeating-linear-gradient(45deg, var(--lit-edge) 0 12px, #e2d3b8 12px 24px);
  border: var(--lead-w) solid var(--lead);
  display: grid;
  place-items: center;
  min-height: 16rem;
  color: var(--ink-soft);
  font-family: var(--display);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-align: center;
  padding: 1.5rem;
}

/* --- Footer --------------------------------------------------------------- */

.footer {
  background: var(--ground-lo);
  border-top: var(--lead-w) solid var(--lead);
  padding-block: clamp(2.5rem, 6vw, 4rem);
}
.footer__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  gap: 2.5rem;
}

/* The full lockup, dark variant. Only place on the site where the whole mark
   appears -- the masthead pairs the lion with the name set in Josefin, which
   would collide with the lockup's own wordmark anywhere nearer to it.

   It rides in the grid as a fourth column rather than sitting above it, which
   is what freed the row: "Operated by" moved down to the base line, where a
   single link never justified a column heading of its own. Nudged up slightly
   because the lockup's own optical top sits below its box. */
.footer__mark { margin-top: -0.5rem; }
.footer__mark img { width: min(100%, 15rem); }
.footer h2 {
  font-family: var(--display);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 1rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; }
.footer li + li { margin-top: 0.5rem; }
.footer a { color: var(--on-dark-soft); text-decoration: none; }
.footer a:hover { color: var(--on-dark); text-decoration: underline; }
.footer address { font-style: normal; color: var(--on-dark-soft); line-height: 1.55; }
.footer__base {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--lead-soft);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  font-size: 0.875rem;
  color: var(--on-dark-soft);
}

/* --- Responsive ----------------------------------------------------------- */

@media (max-width: 60rem) {
  .hero__inner { grid-template-columns: 1fr; }
  .hero__mark { order: -1; width: min(100%, 15rem); }
  .priority { grid-template-columns: 1fr; gap: 0.75rem; }
  /* Stacked and centred below a wide screen: the logo box takes the middle of
     the band, the name and line sit under it. The :has() rules that widen the
     logo column also set grid-template-columns, so this override has to repeat
     them -- a bare .partner-head__inner loses to them on specificity and the
     name gets squeezed into a sliver at the right edge of a phone. */
  .partner-head__inner,
  .partner-head__inner:has(.partner-head__logo--panel),
  .partner-head__inner:has(.partner-head__logo--wash),
  .partner-head__inner:has(.partner-head__logo--shade) {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
  }
  .partner-head__logo { width: min(100%, 14rem); }
  .partner-head__logo--panel,
  .partner-head__logo--wash,
  .partner-head__logo--shade { width: min(100%, 20rem); }
  .partner-head__line { margin-inline: auto; }
}

@media (max-width: 48rem) {
  .window { grid-template-columns: 1fr; }
  .pane--wide, .pane--half, .pane--third { grid-column: span 1; }
  .pane--tall { grid-row: span 1; }
  .pane { min-height: 12rem; }

  /* The nav wraps to two rows on a phone. Pinned, that costs a quarter of the
     screen on every scroll, so it rides along instead. */
  .masthead { position: static; }
  .masthead__inner { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
  .nav { margin-left: -0.5rem; }
  .nav a { padding: 0.45rem 0.5rem; font-size: 0.78125rem; letter-spacing: 0.09em; }
  .brand img { width: 2.5rem; height: 2.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .btn:hover { transform: none; }
}
