/* ==========================================================================
   CALTECH ALPINE CLUB
   One stylesheet, organized top to bottom. No framework, no build step.

     1. Design tokens        the colors, type sizes and spacing, all in one
     2. Reset and base       sensible defaults
     3. Typography           headings, lede, prose
     4. Layout               wrap, section, grids
     5. Buttons and links
     6. Masthead and nav
     7. Hero
     8. Event cards
     9. Resource cards
    10. Credibility, sponsors, officers
    11. Join steps
    12. Footer
    13. Utilities, motion, print

   CHANGING THE LOOK: almost everything worth changing is in section 1.
   Edit a token there and it updates consistently across the whole site.
   ========================================================================== */


/* ==========================================================================
   1. DESIGN TOKENS
   ========================================================================== */

:root {
  /* --- Color ------------------------------------------------------------
     Written in HSL rather than hex so the shades are readable and derivable:
     same hue, walk the lightness. Granite and warm paper, with alpenglow
     doing all the pointing. Used sparingly on purpose — when everything is
     accented, nothing is.

     TWO ACCENTS, and the difference is not cosmetic:
       --alpenglow    fills, buttons, icons, large text, decoration
       --accent-text  accent TEXT at small sizes. The bright accent reaches
                      only 4.4:1 on paper and 4.0:1 on the tint, both under
                      the WCAG AA floor of 4.5:1, so small accent text uses
                      the darker one (6.1:1 and 5.6:1). Adding accent text
                      anywhere? Use --accent-text.                           */
  --ink:            hsl(200 13%  9%);   /* near-black, very slightly cool  */
  --ink-soft:       hsl(195 12% 13%);
  --ink-line:       hsl(196 11% 20%);

  --paper:          hsl( 38 44% 96%);   /* warm off-white page background  */
  --paper-2:        hsl( 35 41% 92%);   /* tinted band                     */
  --white:          hsl(  0  0% 100%);

  --line:           hsl( 37 25% 86%);
  --line-strong:    hsl( 36 21% 77%);

  --text:           hsl(195 11% 14%);
  --text-mute:      hsl(197  7% 41%);
  --on-dark:        hsl( 40 28% 90%);
  --on-dark-mute:   hsl(130  3% 65%);

  --alpenglow:      hsl( 15 62% 46%);   /* the accent — late light on rock */
  --alpenglow-dark: hsl( 16 69% 37%);
  --alpenglow-tint: hsl( 22 58% 92%);
  --accent-text:    var(--alpenglow-dark);
  /* HOVER for accent TEXT. Four hover rules used to brighten to --alpenglow,
     which is 4.43:1 on paper and only 4.07:1 on the tinted band — i.e. the
     link got HARDER to read at the moment of interaction, and broke the rule
     stated three lines above. This is the brightest the accent can go and
     still clear 4.5:1 everywhere it appears (4.94 paper / 4.54 tint /
     5.33 card), while staying visibly lighter than --alpenglow-dark. */
  --alpenglow-hover: hsl( 15 62% 43%);
  /* On the ink background the accent has to go LIGHTER, not darker: the base
     accent only manages 3.3:1 against ink, so hovers and eyebrows on dark
     use this (6.5:1) instead. */
  --accent-on-dark: hsl( 18 68% 64%);
  --glacier:        hsl(190 42% 32%);

  /* --- Type --------------------------------------------------------------
     ONE family, and it is the whole typographic system: Inter, everywhere —
     body, navigation, buttons, cards, labels, footer and every heading.
     Character comes from WEIGHT, SCALE and SPACING rather than from
     collecting fonts.

     WHY NO DISPLAY FACE (2026-08-31). The club's mark already contains a
     strong condensed bold wordmark. Setting the headings in a second display
     family would put two typographic identities on the same page and make
     the site argue with its own logo. The website is the quiet half of that
     pair on purpose. Adding Barlow Condensed, Anton, Oswald or similar here
     is the change this comment exists to prevent.

     If Inter fails to load, the stack below is a close enough grotesque that
     nothing reflows badly. Loaded as a single variable file — see the
     comment above the <link> in includes/header.php.                        */
  --font-sans: 'Inter', 'Helvetica Neue', Helvetica, Arial, sans-serif;

  /* --- Tracking ----------------------------------------------------------
     Optical letter-spacing, keyed to SIZE rather than to element: the bigger
     the type, the tighter it sets. These are the only tracking values on the
     site outside the uppercase labels, and a heading picks the one that
     matches its step. Body text takes a hair off the default, which is where
     Inter wants to be at 16-17px.                                           */
  --track-body:    -0.008em;
  --track-h4:      -0.008em;   /* step-1 */
  --track-h3:      -0.013em;   /* step-2 */
  --track-h2:      -0.020em;   /* step-3 */
  --track-h1:      -0.025em;   /* step-4 */
  --track-display: -0.032em;   /* step-5 */
  --track-label:    0.12em;    /* uppercase eyebrows and labels only         */

  --step--1: clamp(0.82rem, 0.80rem + 0.10vw, 0.88rem);
  --step-0:  clamp(1.00rem, 0.97rem + 0.16vw, 1.08rem);
  --step-1:  clamp(1.15rem, 1.08rem + 0.34vw, 1.35rem);
  --step-2:  clamp(1.40rem, 1.22rem + 0.85vw, 1.95rem);
  --step-3:  clamp(1.75rem, 1.40rem + 1.60vw, 2.70rem);
  --step-4:  clamp(2.15rem, 1.55rem + 2.90vw, 3.80rem);
  --step-5:  clamp(2.70rem, 1.60rem + 5.00vw, 5.40rem);

  /* --- Space and shape --------------------------------------------------- */
  --wrap:      1220px;
  --gutter:    clamp(1.15rem, 4vw, 2.75rem);
  --section-y: clamp(3.5rem, 8vw, 7rem);

  --r-sm: 3px;
  --r:    6px;
  --r-lg: 10px;

  /* Cards are white on warm paper, so they already separate themselves. A soft
     shadow finishes the job a border used to do, without drawing a hard line
     around every element on the page. */
  --shadow:      0 1px 2px hsl(200 13% 9% / .05);
  --shadow-card: 0 1px 2px hsl(200 13% 9% / .04), 0 4px 14px -6px hsl(200 13% 9% / .10);
  --shadow-up:   0 2px 4px hsl(200 13% 9% / .05), 0 14px 32px -12px hsl(200 13% 9% / .20);

  --speed: 160ms;
  --ease:  cubic-bezier(.2, .6, .3, 1);

  /* --- Background artwork -------------------------------------------------
     The faint contour lines behind dark sections. This is a real topographic
     map of Mount Wilson and the Mt Wilson Trail, computed from public-domain
     USGS elevation data by tools/make_topo.py — not a stock texture.

     Point it at anywhere else the club goes by editing the coordinates in
     that script and re-running it. */
  --topo-image: url('../images/mt-wilson-topo.svg');
}


/* ==========================================================================
   2. RESET AND BASE
   ========================================================================== */

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

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

/* Anchored sections must not hide under the sticky masthead. */
:target { scroll-margin-top: 6rem; }
[id]    { scroll-margin-top: 6rem; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.6;
  letter-spacing: var(--track-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a { color: inherit; }

button { font: inherit; color: inherit; }

ul, ol { margin: 0; padding: 0; }
li { list-style: none; }

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 0;
}

/* A visible, high-contrast focus ring everywhere. Never remove this. */
:focus-visible {
  outline: 3px solid var(--alpenglow);
  outline-offset: 2px;
  border-radius: 2px;
}
.masthead :focus-visible,
.hero :focus-visible,
.site-footer :focus-visible {
  outline-color: var(--accent-on-dark);
}

/* The icon sprite itself is never seen. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.icon {
  width: 1.15em; height: 1.15em;
  flex: none;
  vertical-align: -0.18em;
}
.icon--xs { width: .8em;  height: .8em; }
.icon--lg { width: 1.6em; height: 1.6em; }
.icon--xl { width: 2.4em; height: 2.4em; }


/* ==========================================================================
   3. TYPOGRAPHY
   ========================================================================== */

/* HIERARCHY COMES FROM SIZE, WEIGHT AND TRACKING — never from a second font
   family. Three things vary together down the scale: the type gets smaller,
   it gets LIGHTER, and it tracks LOOSER. Nothing here is above 750, because
   the mark in the masthead is the heaviest thing on the page and it should
   stay that way.

   WEIGHT AND TRACKING KEY OFF THE SIZE CLASS, NOT THE ELEMENT. The site
   routinely writes <h2 class="h3"> to keep the outline correct while setting
   the type a step down, so an element-driven rule would give that heading h2
   weight at h3 size. The element rule below is only the fallback for a
   heading that names no size class. */
h1, h2, h3, h4 {
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: var(--track-h2);
  text-wrap: balance;
}

/* The biggest size on the site. One user: the homepage h1. Right for a two- or
   three-word line, wrong for a sentence -- see .hero__title in section 7.

   750, not 800. The masthead logo is the heaviest thing on any page here and
   should stay that way; the hero name sits just under it rather than beside
   it. Tracking is -0.032em because Inter at 5rem needs the air taken out. */
.display {
  font-size: var(--step-5);
  font-weight: 750;
  line-height: 1.0;
  letter-spacing: var(--track-display);
}

.h1 { font-size: var(--step-4); font-weight: 720; letter-spacing: var(--track-h1); }
.h2 { font-size: var(--step-3); font-weight: 700; letter-spacing: var(--track-h2); }
.h3 { font-size: var(--step-2); font-weight: 670; letter-spacing: var(--track-h3); }
.h4 { font-size: var(--step-1); font-weight: 650; letter-spacing: var(--track-h4); }

/* Headings whose size comes from a component rule rather than a size class.
   They get the treatment that matches the STEP they are set at, so a step-1
   heading tracks like .h4 wherever it turns up and the page reads as one
   system. Add to these lists rather than inventing a third setting. */
.prose h2, .step h2, .empty-state h3 {
  font-weight: 670;
  letter-spacing: var(--track-h3);
}
.prose h3, .pillar h3 {
  font-weight: 650;
  letter-spacing: var(--track-h4);
}

/* The small capitalised label above a heading. Used a lot; keep it quiet.
   Uppercase is the ONE place tracking goes positive — caps need the air that
   lowercase does not. .12em sits mid-range; it was .15em, which read as
   spaced-out rather than as a label. Ordinary headings are never uppercased. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: .5em;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: .9rem;
}
.section--dark .eyebrow { color: var(--accent-on-dark); }

.lede {
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--text-mute);
  max-width: 46ch;
  text-wrap: pretty;
}
/* Any dark ground. This used to key off `.on-dark`, which left every .lede
   inside a .section--dark at 3.3:1 — gray text on near-black, under the AA
   floor. `.on-dark` is gone now: no template ever set it. */
.section--dark .lede,
.section--dark .step__hint { color: var(--on-dark-mute); }

.prose { max-width: 62ch; }
.prose > * + * { margin-top: 1.1em; }
.prose a { color: var(--accent-text); text-underline-offset: .2em; }
.prose a:hover { color: var(--alpenglow-hover); }
.prose strong { font-weight: 600; }
.prose h3 { font-size: var(--step-1); margin-top: 2em; }
.prose ul { margin-top: 1em; }
.prose ul li {
  list-style: none;
  position: relative;
  padding-left: 1.4em;
  margin-top: .5em;
}
.prose ul li::before {
  content: '';
  position: absolute;
  left: .25em; top: .72em;
  width: .45em; height: .45em;
  background: var(--alpenglow);
  border-radius: 50%;
}

/* --- long-form prose ---------------------------------------------------- */

.prose h2 {
  font-size: var(--step-2);
  margin-top: 1.8em;
  padding-top: .4em;
}
.prose h2 + * { margin-top: .7em; }
.prose h3 + * { margin-top: .6em; }

.prose ol {
  margin-top: 1em;
  padding-left: 1.5em;
}
.prose ol li {
  list-style: decimal;
  margin-top: .5em;
  padding-left: .2em;
}

.prose img {
  margin-block: 1.6em;
  border: 1px solid var(--line);
  border-radius: var(--r);
}

.prose blockquote {
  margin-block: 1.5em;
  padding-left: 1.15rem;
  border-left: 3px solid var(--line-strong);
  color: var(--text-mute);
}

/* Guides were written over many years by different people; some open with a
   heading and some with a paragraph. Kill the leading gap either way. */




/* ==========================================================================
   4. LAYOUT
   ========================================================================== */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: 820px; }

.section { padding-block: var(--section-y); }
.section--tight { padding-block: calc(var(--section-y) * .6); }

.section--tint { background: var(--paper-2); }

.section--dark {
  position: relative;
  background: var(--ink);
  color: var(--on-dark);
  overflow: hidden;
}
.section--dark h1, .section--dark h2, .section--dark h3 { color: var(--white); }

/* Section heading block: eyebrow + title on the left, a link on the right. */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: 1.25rem 2rem;
  margin-bottom: clamp(1.75rem, 4vw, 3rem);
}
.section-head__text { max-width: 42ch; }
.section-head p.lede { margin-top: .85rem; }

/* The drawn contour map. Tinted by `color`, so it works on any background.
   Hidden entirely where CSS masks are unsupported rather than showing a
   solid block. */
.topo {
  display: none;
  position: absolute;
  inset: 0;
  pointer-events: none;
}
@supports ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  .topo {
    display: block;
    background-color: currentColor;
    -webkit-mask: var(--topo-image) center / cover no-repeat;
            mask: var(--topo-image) center / cover no-repeat;
  }
}
.section--dark .topo { color: #ffffff; opacity: .055; }
.section--dark > .wrap { position: relative; }


/* ==========================================================================
   5. BUTTONS AND LINKS
   ========================================================================== */

.btn {
  --btn-bg:     var(--ink);
  --btn-fg:     var(--white);
  --btn-border: var(--ink);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55em;

  padding: .82em 1.5em;
  border: 1px solid var(--btn-border);
  border-radius: var(--r-sm);
  background: var(--btn-bg);
  color: var(--btn-fg);

  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;

  transition: background var(--speed) var(--ease),
              border-color var(--speed) var(--ease),
              color var(--speed) var(--ease),
              transform var(--speed) var(--ease);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  --btn-bg: var(--alpenglow);
  --btn-border: var(--alpenglow);
}
.btn--primary:hover {
  --btn-bg: var(--alpenglow-dark);
  --btn-border: var(--alpenglow-dark);
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  --btn-border: var(--line-strong);
}
.btn--ghost:hover { --btn-border: var(--ink); --btn-bg: var(--white); }

/* On dark backgrounds. */
.btn--light {
  --btn-bg: transparent;
  --btn-fg: var(--white);
  --btn-border: rgba(255, 255, 255, .38);
}
.btn--light:hover { --btn-bg: rgba(255, 255, 255, .1); --btn-border: #fff; }

.btn--sm { padding: .58em 1.05em; font-size: var(--step--1); }
.btn--lg { padding: .95em 1.9em; font-size: var(--step-1); }
.btn--block { display: flex; width: 100%; }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
}

/* Click-to-copy address. site.js swaps every printed email address for one of
   these; it has to sit inside a sentence without looking like a control, so it
   inherits everything from the link it replaced and only announces itself on
   hover. */
.copy {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--accent-text);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: .2em;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
  word-break: break-all;
  transition: color var(--speed) var(--ease);
}
.copy::after {
  content: '';
  display: inline-block;
  width: .82em;
  height: .82em;
  margin-left: .38em;
  vertical-align: -.06em;
  opacity: .45;
  background: currentColor;
  transition: opacity var(--speed) var(--ease);
  /* two offset rounded squares — the usual copy glyph */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linejoin='round'%3E%3Crect x='9' y='9' width='12' height='12' rx='2'/%3E%3Cpath d='M5 15V5a2 2 0 0 1 2-2h10'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linejoin='round'%3E%3Crect x='9' y='9' width='12' height='12' rx='2'/%3E%3Cpath d='M5 15V5a2 2 0 0 1 2-2h10'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* A button that carries its own eligibility warning, on the assumption that a
   visitor reads the buttons and nothing else. The restriction has to travel
   WITH the control it restricts — a paragraph further up the page is not read
   by the person about to click. Stacks the label over the gate line, so the
   gate is legible rather than a parenthesis nobody parses.

   No opacity on the gate line: white on the accent fill is 4.77:1, and fading
   it would drop that under the AA floor at the exact size where it matters.

   These came off all three gear buttons on 2026-08-31 as a duplicate of the
   callout above them, and went back the same day (Kyle). The redundancy
   argument is real and it loses to the failure it prevents: somebody who
   clicks straight through to the Caltech Y booking system, having read no
   prose at all, and finds out at the counter. Removing the surrounding
   repetition is the fix; removing it from the control is not. */
.btn--gated { flex-direction: column; gap: .3em; padding-block: .72em; }
.btn__label { display: inline-flex; align-items: center; gap: .55em; }
.btn__gate {
  font-size: .72em;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.copy:hover { color: var(--alpenglow-hover); }
.copy:hover::after { opacity: .9; }

/* The variant that sits NEXT TO a surviving mailto link rather than replacing
   it — see the "copyable emails" section of site.js. It is the glyph alone, so
   it needs a hit area of its own and must not inherit the link's underline. */
.copy--beside {
  margin-left: .1em;
  padding: .35em;
  text-decoration: none;
  vertical-align: -.25em;
}
.copy--beside::after { margin-left: 0; }
.copy--beside.is-copied::after { opacity: 1; }
.copy--beside.is-copied { color: var(--glacier); }

.copy.is-copied {
  color: #1d7a4c;
  text-decoration: none;
}
.copy.is-copied::after {
  opacity: 1;
  /* a tick, while the confirmation is showing */
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6.5'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5 9.5 18 20 6.5'/%3E%3C/svg%3E");
}

/* On dark ground (the footer) it has to read as light text, not accent. */
.site-footer .copy { color: var(--white); text-decoration-color: #46504f; }
.site-footer .copy:hover { color: var(--accent-on-dark); }
.site-footer .copy.is-copied { color: #7fd0a3; }

/* An "email us" BUTTON used to live here. It was removed: a mailto button is
   the wrong shape for the job, because plenty of people read mail in a browser
   tab where the link does nothing, and everything you asked them to say was
   hidden inside it. The replacement writes the instructions out as text —
   see .write-to in section 10. */

/* A quiet text link with a moving arrow. */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  font-weight: 600;
  text-decoration: none;
  color: var(--accent-text);
  white-space: nowrap;
}
.arrow-link .icon { transition: transform var(--speed) var(--ease); }
.arrow-link:hover { color: var(--alpenglow-hover); }
.arrow-link:hover .icon { transform: translateX(3px); }
.on-dark .arrow-link, .section--dark .arrow-link { color: var(--accent-on-dark); }


/* ==========================================================================
   6. MASTHEAD AND NAVIGATION
   ========================================================================== */

.skip-link {
  position: absolute;
  left: 50%; top: 0;
  transform: translate(-50%, -120%);
  z-index: 200;
  padding: .7rem 1.2rem;
  background: var(--alpenglow);
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  border-radius: 0 0 var(--r) var(--r);
  transition: transform var(--speed) var(--ease);
}
.skip-link:focus { transform: translate(-50%, 0); }

.masthead {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: var(--on-dark);
  border-bottom: 1px solid var(--ink-line);
}

.masthead__inner {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  min-height: 4.4rem;
}

/* --- brand --- */
.brand {
  display: flex;
  align-items: center;
  gap: .7rem;
  text-decoration: none;
  color: var(--white);
  margin-right: auto;
  padding-block: .55rem;
}
/* SIZED BY HEIGHT, width auto. The logo is a wide lockup (3.492:1), so fixing
   both dimensions would squash it, and fixing the width would make the type
   size depend on how much room the bar happens to have.

   2.75rem is 44px, which puts "ALPINE CLUB" at a 13.6px cap height and
   "CALTECH" at 14.6px. Measured off art/logo.png, not guessed: those lines are
   30.9% and 33.3% of the artwork's height.

   THE SMALL-LINE CAVEAT IS GONE (2026-08-31). The previous drawing set
   "CALTECH" at 12.8% of the art, i.e. 5.6px in this bar -- texture rather than
   type, and unreadable on a phone. In this drawing the two lines are within a
   few percent of each other, so there is no longer a size below which half the
   name disappears first. If the aspect changes again, re-measure rather than
   assuming, and update the width in includes/header.php with it.

   The colours live in the SVG, not here: it is loaded with <img>, so it cannot
   inherit anything from this stylesheet. That is why the dark bar gets
   logo-on-dark.svg rather than a filter. */
.brand__logo { height: 2.75rem; width: auto; }
/* The fallback when site.logo_dark is empty. Not rendered otherwise. */
.brand__text { display: grid; line-height: 1.05; }
.brand__caltech {
  font-size: .64rem;
  font-weight: 600;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--on-dark-mute);
}
.brand__name {
  font-size: 1.12rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* --- desktop nav --- */
.nav { display: none; }
.nav__list { display: flex; align-items: center; gap: .15rem; }
.nav__item { display: flex; align-items: center; }

.nav__link {
  display: block;
  padding: .55rem .7rem;
  border-radius: var(--r-sm);
  color: var(--on-dark);
  font-size: .93rem;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.nav__link:hover { color: var(--white); background: rgba(255, 255, 255, .07); }
.nav__link.is-current { color: var(--white); }
.nav__link.is-current::after {
  content: '';
  display: block;
  height: 2px;
  margin-top: .3rem;
  background: var(--alpenglow);
  border-radius: 2px;
}



.masthead__cta { display: none; }

/* --- mobile toggle + panel --- */
.masthead__toggle {
  display: grid;
  place-items: center;
  width: 2.6rem; height: 2.6rem;
  margin-right: -.5rem;
  padding: 0;
  border: 0;
  background: none;
  color: var(--white);
  cursor: pointer;
  border-radius: var(--r-sm);
}
.masthead__toggle-close { display: none; }
.masthead__toggle[aria-expanded="true"] .masthead__toggle-open  { display: none; }
.masthead__toggle[aria-expanded="true"] .masthead__toggle-close { display: block; }

.mobile-nav {
  position: fixed;
  inset: 4.4rem 0 0;
  z-index: 99;
  overflow-y: auto;
  background: var(--ink);
  border-top: 1px solid var(--ink-line);
  padding-block: 1.5rem 3rem;
  overscroll-behavior: contain;
}
.mobile-nav[hidden] { display: none; }
.mobile-nav__list { margin-bottom: 1.75rem; }
.mobile-nav__list > li + li {
  margin-top: .25rem;
  padding-top: .25rem;
  border-top: 1px solid var(--ink-line);
}
.mobile-nav__top {
  display: block;
  padding: .8rem 0 .55rem;
  color: var(--white);
  font-size: 1.22rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-decoration: none;
}
.mobile-nav__sub {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem .5rem;
  padding-bottom: .9rem;
}
.mobile-nav__sub a {
  display: block;
  padding: .34rem .7rem;
  border: 1px solid var(--ink-line);
  border-radius: 100px;
  color: var(--on-dark-mute);
  font-size: .86rem;
  text-decoration: none;
}
.mobile-nav__sub a:hover { color: var(--white); border-color: #4a5457; }

body.nav-open { overflow: hidden; }

@media (min-width: 1060px) {
  .nav { display: block; }
  .masthead__cta { display: inline-flex; }
  .masthead__toggle { display: none; }
  .mobile-nav { display: none !important; }
}


/* ==========================================================================
   7. HERO
   ========================================================================== */

.hero {
  position: relative;
  background: var(--ink);
  color: var(--on-dark);
  overflow: hidden;
  isolation: isolate;
}

/* The photograph, when there is one. */
.hero__media {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center 42%;
  z-index: -2;
}

/* Darkening scrim so text stays legible over any photo we are handed. */
.hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, rgba(14,17,19,.90) 0%, rgba(14,17,19,.62) 48%, rgba(14,17,19,.35) 100%),
    linear-gradient(to top,   rgba(14,17,19,.75) 0%, rgba(14,17,19,0) 55%);
}

.hero__topo { z-index: -1; color: #fff; opacity: .075; }

.hero__inner {
  position: relative;
  display: grid;
  align-content: center;
  min-height: clamp(30rem, 76vh, 46rem);
  padding-block: clamp(4rem, 10vw, 7rem);
  max-width: 60rem;
}

/* THE HERO IS LIVE TEXT, NOT THE ARTWORK (2026-08-31).

   The horizontal mark was tried as the hero headline and dropped: the masthead
   already carries it, and repeating it 80px lower made the top of the page
   read as the same object twice. So the club's name is set as type again, and
   the artwork stays in the bar where it identifies every page rather than one.

   WHAT DID CHANGE is the level above it. The hero used to open with a spaced
   uppercase eyebrow -- "PASADENA, CALIFORNIA · FOUNDED 1987" -- putting four
   typographic levels between the visitor and a button. It is gone. The
   founding year is still on this page, in the About band further down, which
   is where a historical fact belongs; it was never worth the line above the
   club's own name.

   The name is set in Inter, like everything else here. That is not a
   compromise for the sake of one family: the logo's own wordmark is a bold
   neo-grotesque -- flat-cut terminals, moderate width, not condensed -- so
   Inter at 750 is the closest live-text match to it that exists. A condensed
   display face would have been the thing that clashed. */
.hero__title {
  color: var(--white);
  margin-bottom: 1.1rem;
}

/* The line under the mark: what the club is, in one sentence. It is the only
   introductory sentence in the hero -- there is no second paragraph under the
   buttons, and adding one is what this comment exists to prevent.

   IT MUST NOT COMPETE WITH THE LOGO. Noticeably larger than body copy and
   much quieter than the artwork: 500 rather than 600+, the page's own light
   text colour rather than the accent, sentence case, no tracking games. */
.hero__text {
  font-size: var(--step-1);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: var(--track-body);
  color: var(--on-dark);
  opacity: .92;
  max-width: 46ch;
  margin-bottom: 2.5rem;
  text-wrap: pretty;
}

.hero__actions { gap: .8rem; }

/* Small print under the hero buttons — the "no experience needed" reassurance. */
.hero__note {
  margin-top: 1.5rem;
  font-size: var(--step--1);
  color: var(--on-dark-mute);
  display: flex;
  align-items: center;
  gap: .5em;
}

/* Compact hero for interior pages. */
.page-hero {
  position: relative;
  background: var(--ink);
  color: var(--on-dark);
  overflow: hidden;
  padding-block: clamp(3rem, 7vw, 5.25rem);
}
.page-hero .topo { color: #fff; opacity: .06; }
.page-hero__inner { position: relative; max-width: 52rem; }
.page-hero h1 { color: var(--white); margin-bottom: .9rem; }
.page-hero .lede { color: var(--on-dark-mute); max-width: 52ch; }


/* ==========================================================================
   8. EVENT CARDS
   ========================================================================== */

.events-grid {
  display: grid;
  gap: 1.1rem;
  /* auto-FIT, not auto-fill: fill leaves the empty tracks in place, so a
     single upcoming event sat in one column with two thirds of the row blank
     beside it — which reads as "nothing is happening" on the one section
     meant to prove the opposite. fit collapses the unused tracks. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}

/* A lone card should not stretch the full width either; cap it so it still
   looks like a card rather than a banner. */
.events-grid > .event-card:only-child { max-width: 34rem; }

.event-card {
  display: flex;
  gap: 1.15rem;
  padding: 1.35rem;
  background: var(--white);
  border-radius: var(--r);
  box-shadow: var(--shadow-card);
  transition: border-color var(--speed) var(--ease),
              box-shadow var(--speed) var(--ease),
              transform var(--speed) var(--ease);
}
.event-card:hover {
  box-shadow: var(--shadow-up);
  transform: translateY(-2px);
}
.section--dark .event-card {
  background: var(--ink-soft);
  border-color: var(--ink-line);
}
.section--dark .event-card:hover { border-color: #46504f; }

/* The date block. Reads at a glance, which is the whole point. */
.event-card__date {
  flex: none;
  display: grid;
  place-content: center start;
  width: 3.6rem;
  padding-top: .1rem;
  border-right: 1px solid var(--line);
  padding-right: 1rem;
}
.section--dark .event-card__date { border-right-color: var(--ink-line); }

.event-card__month {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-text);
}
.event-card__day {
  font-size: 1.85rem;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.04em;
  color: var(--text);
}
.section--dark .event-card__day { color: var(--white); }
.event-card__year {
  font-size: .7rem;
  color: var(--text-mute);
  letter-spacing: .04em;
}
.section--dark .event-card__year { color: var(--on-dark-mute); }

.event-card__body { min-width: 0; display: flex; flex-direction: column; }

.event-card__title {
  font-size: var(--step-1);
  font-weight: 700;
  line-height: 1.2;
  margin-bottom: .55rem;
}
.section--dark .event-card__title { color: var(--white); }

.event-card__meta {
  display: flex;
  align-items: flex-start;
  gap: .45em;
  font-size: .89rem;
  color: var(--text-mute);
  margin-bottom: .2rem;
}
.section--dark .event-card__meta { color: var(--on-dark-mute); }
.event-card__meta .icon { margin-top: .18em; opacity: .7; }

/* "Weekly on Tuesdays" — marks a card as the next of a repeating series. */
.event-card__repeat {
  display: inline-block;
  margin-top: .3rem;
  padding: .1rem .5rem;
  border: 1px solid var(--line-strong);
  border-radius: 100px;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--text-mute);
  white-space: nowrap;
}
.section--dark .event-card__repeat { border-color: #46504f; color: var(--on-dark-mute); }

.event-card__excerpt {
  margin-top: .7rem;
  font-size: .92rem;
  line-height: 1.55;
  color: var(--text-mute);
}
.section--dark .event-card__excerpt { color: var(--on-dark-mute); }

.event-card__foot {
  margin-top: auto;
  padding-top: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem 1.1rem;
}

/* Cards that have more to show become clickable. The button below is the real
   control; this just makes the obvious gesture work too. */
.event-card--openable { cursor: pointer; }

.event-card__details {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: .89rem;
  font-weight: 600;
  color: var(--accent-text);
  cursor: pointer;
  white-space: nowrap;
}
.event-card__details .icon { transition: transform var(--speed) var(--ease); }
.event-card__details:hover { color: var(--alpenglow-hover); }
.event-card__details:hover .icon { transform: translateX(3px); }
.section--dark .event-card__details { color: var(--accent-on-dark); }

.event-card__cal { font-size: .89rem; font-weight: 500; color: var(--text-mute); }
.event-card__cal:hover { color: var(--accent-text); }
.section--dark .event-card__cal { color: var(--on-dark-mute); }

/* --- the event dialog --- */
.event-dialog {
  width: min(38rem, calc(100vw - 2rem));
  max-height: min(85vh, 44rem);
  padding: 0;
  border-radius: var(--r);
  background: var(--white);
  color: var(--text);
  box-shadow: 0 24px 60px -18px rgba(20, 24, 26, .5);
  overflow: visible;
}
.event-dialog::backdrop { background: rgba(14, 17, 19, .62); }

.event-dialog__close-form { position: absolute; top: .6rem; right: .6rem; margin: 0; }
.event-dialog__close {
  display: grid;
  place-items: center;
  width: 2.4rem; height: 2.4rem;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-mute);
  cursor: pointer;
}
.event-dialog__close:hover { background: var(--paper-2); color: var(--ink); }

.event-dialog__body {
  padding: clamp(1.5rem, 4vw, 2.25rem);
  overflow-y: auto;
  max-height: min(85vh, 44rem);
}
/* A <p>, not a heading — see alpine_event_dialog() for why — so it has to
   restate the weight and tracking it used to inherit from the h1-h4 rule. */
.event-dialog__title {
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.022em;
  text-wrap: balance;
  margin-bottom: .9rem;
}
.event-dialog__meta {
  display: flex;
  align-items: flex-start;
  gap: .45em;
  font-size: .94rem;
  color: var(--text-mute);
  margin-bottom: .3rem;
}
.event-dialog__meta .icon { margin-top: .2em; opacity: .7; }
.event-dialog__text {
  margin-top: 1.4rem;
  font-size: .95rem;
}
.event-dialog__text:empty { display: none; }
.event-dialog__text + .btn-row { margin-top: 1.6rem; }

.event-card--cancelled { opacity: .72; }
.event-card__cancelled {
  display: inline-block;
  margin-bottom: .5rem;
  padding: .12rem .5rem;
  border: 1px solid currentColor;
  border-radius: 100px;
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  /* NOTE: a hex literal, which nothing else in this file is. It measures
     5.66:1 on a white card, so it passes AA and was left alone by the
     typography pass -- but it belongs in a token. See tools/palette.py. */
  color: #b4402c;
}
.event-card--cancelled .event-card__title { text-decoration: line-through; }

/* Full-width row variant, used on the events page archive. */
.event-row {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 1.25rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--line);
}
.event-row:last-child { border-bottom: 1px solid var(--line); }
.event-row__when {
  font-size: .84rem;
  font-weight: 600;
  color: var(--text-mute);
  letter-spacing: .02em;
  padding-top: .18rem;
}
.event-row__title { font-size: var(--step-1); font-weight: 600; line-height: 1.25; }
.event-row__meta { font-size: .88rem; color: var(--text-mute); margin-top: .3rem; }

/* Rows with a description open the same pop-up the cards do. */
.event-row--openable { cursor: pointer; }
.event-row--openable:hover .event-row__title { color: var(--accent-text); }
.event-row__details {
  display: inline-flex;
  align-items: center;
  gap: .35em;
  margin-top: .45rem;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: .84rem;
  font-weight: 600;
  color: var(--accent-text);
  cursor: pointer;
  white-space: nowrap;
}
.event-row__details .icon { transition: transform var(--speed) var(--ease); }
.event-row__details:hover .icon { transform: translateX(3px); }

/* The state that matters most on launch day: nothing scheduled yet. */
.empty-state {
  padding: clamp(2rem, 5vw, 3.25rem);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r);
  background: var(--white);
  text-align: center;
}
.section--dark .empty-state {
  background: rgba(255, 255, 255, .03);
  border-color: #3a4346;
}
.empty-state__icon { margin: 0 auto .9rem; color: var(--line-strong); }
.section--dark .empty-state__icon { color: #4d5658; }
.empty-state h3 { font-size: var(--step-2); margin-bottom: .6rem; }
.empty-state p {
  max-width: 46ch;
  margin: 0 auto 1.4rem;
  color: var(--text-mute);
}
.section--dark .empty-state p { color: var(--on-dark-mute); }
.empty-state .btn-row { justify-content: center; }

/* Calendar embed — only ever a fallback / a convenience, never the main event. */
.calendar-embed {
  position: relative;
  border-radius: var(--r);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  background: var(--white);
}
.calendar-embed iframe {
  display: block;
  width: 100%;
  min-height: 620px;
  border: 0;
}
@media (max-width: 600px) {
  .calendar-embed iframe { min-height: 480px; }
}

.subscribe-note {
  margin-top: .9rem;
  margin-bottom: 1.5rem;
  font-size: .9rem;
  color: var(--text-mute);
}


/* ==========================================================================
   9. RESOURCE CARDS
   ========================================================================== */



/* ==========================================================================
   10. CREDIBILITY, SPONSORS, OFFICERS
   ========================================================================== */

/* Three cards, and the number is a decision rather than an accident -- see the
   comment above the grid in index.php. So the layout is stated, not derived:
   three across when there is room, stacked when there is not, and never a
   half-empty row. `auto-fit` was doing the derivation and it broke on three:
   between roughly 640px and 900px it settled on TWO columns, which left the
   third card alone beside a hole. Measured at 700px, 2026-08-30. */
.pillars {
  display: grid;
  gap: 1.75rem 2.5rem;
  grid-template-columns: 1fr;
}
@media (min-width: 760px) {
  .pillars { grid-template-columns: repeat(3, 1fr); }
}
.pillar__icon { color: var(--alpenglow); margin-bottom: .8rem; }
.pillar h3 { font-size: var(--step-1); margin-bottom: .45rem; }
.pillar p { font-size: .94rem; color: var(--text-mute); line-height: 1.6; }
.section--dark .pillar p { color: var(--on-dark-mute); }

/* --- sponsors --- */
.sponsors {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  align-items: stretch;
}
.sponsor {
  display: grid;
  place-items: center;
  gap: .5rem;
  min-height: 7.5rem;
  padding: 1.25rem;
  background: var(--white);
  border-radius: var(--r);
  box-shadow: var(--shadow-card);
  text-decoration: none;
  color: inherit;
  text-align: center;
  transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.sponsor:hover { box-shadow: var(--shadow-up); transform: translateY(-2px); }
.sponsor img { max-height: 3.4rem; width: auto; object-fit: contain; }
.sponsor__name { font-weight: 700; font-size: 1.02rem; letter-spacing: -0.015em; }
.sponsor__tier {
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-mute);
}


/* --- gear inventory --- */
.inventory {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.inventory__source {
  padding: clamp(1.4rem, 3vw, 1.9rem);
  background: var(--white);
  border-radius: var(--r);
  box-shadow: var(--shadow-card);
}
.inventory__title {
  font-size: var(--step-1);
  font-weight: 700;
  margin-bottom: .4rem;
}
.inventory__blurb {
  font-size: .9rem;
  color: var(--text-mute);
  line-height: 1.55;
  margin-bottom: 1.4rem;
}
.inventory__group {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-text);
  margin-top: 1.3rem;
  margin-bottom: .5rem;
}
.inventory__group:first-of-type { margin-top: 0; }
.inventory__list li {
  position: relative;
  padding-left: 1.1rem;
  margin-bottom: .35rem;
  font-size: .92rem;
  line-height: 1.5;
}
.inventory__list li::before {
  content: '';
  position: absolute;
  left: 0; top: .62em;
  width: .35em; height: .35em;
  border-radius: 50%;
  background: var(--line-strong);
}

.inventory__foot {
  margin-top: 1.25rem;
  font-size: .88rem;
  color: var(--text-mute);
  max-width: 60ch;
}

/* --- officers --- */
.officer-group + .officer-group { margin-top: 2.75rem; }
.officer-group__title {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--text-mute);
  padding-bottom: .7rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--line);
}
.officers {
  display: grid;
  gap: 1.75rem 1.5rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr));
}

.officer__photo {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--r);
  background: var(--paper-2);
  margin-bottom: .8rem;
}

/* No headshot? Draw their initials. Nobody should be left off the page just
   because they have not sent a photo, and an empty frame looks like a bug. */
.officer__initials {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: var(--r);
  margin-bottom: .8rem;
  background: var(--ink);
  color: var(--white);
  font-size: clamp(1.6rem, 5vw, 2.1rem);
  font-weight: 700;
  letter-spacing: .04em;
}

/* --- past officers ------------------------------------------------------
   A plain list. It grows every year, so it has to stay cheap to read. */
.alumni { margin-top: 3rem; }
.alumni__year + .alumni__year { margin-top: 1.75rem; }
.alumni__heading {
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--accent-text);
  margin-bottom: .7rem;
}
.alumni__list {
  display: grid;
  gap: .3rem 1.5rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
}
.alumni__list li {
  display: flex;
  flex-wrap: wrap;
  gap: .1rem .6rem;
  padding: .35rem 0;
  border-bottom: 1px solid var(--line);
  font-size: .92rem;
}
.alumni__name { font-weight: 600; }
.alumni__role { color: var(--text-mute); }

.officer__name { font-weight: 700; font-size: 1.02rem; letter-spacing: -0.015em; line-height: 1.25; }
.officer__role { font-size: .88rem; color: var(--accent-text); font-weight: 600; margin-top: .1rem; }
.officer__handles { font-size: .85rem; color: var(--text-mute); line-height: 1.45; margin-top: .35rem; }
.officer__mail { display: inline-block; margin-top: .35rem; font-size: .84rem; color: var(--text-mute); }
.officer__mail:hover { color: var(--accent-text); }

/* A quiet framed aside used for the mission line and similar. */
.pull {
  border-left: 3px solid var(--alpenglow);
  padding-left: clamp(1rem, 3vw, 1.75rem);
  font-size: var(--step-2);
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -0.02em;
  max-width: 30ch;
  text-wrap: balance;
}

.split {
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; }
  .split--wide-left { grid-template-columns: 1.25fr 1fr; }
}

/* Contact details as a definition list, addresses shown in full so they can
   be copied. */
.contact-list { display: grid; gap: 0; margin: 0; }
.contact-list > div {
  display: grid;
  gap: .15rem .5rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--line);
}
.contact-list > div:last-child { border-bottom: 1px solid var(--line); }
.contact-list dt {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.contact-list dd { margin: 0; font-size: var(--step-1); font-weight: 600; }
.contact-list dd a { color: var(--accent-text); text-underline-offset: .2em; word-break: break-all; }
.contact-list__note {
  display: block;
  margin-top: .2rem;
  font-size: .88rem;
  font-weight: 400;
  color: var(--text-mute);
}
.contact-list__note a { font-weight: 500; }

/* An email instruction, written out: address, subject line and what to say,
   all as readable text. See alpine_write_to() in includes/partials.php for why
   none of it hides inside a mailto: link. Borrows the contact list's hairline
   rows so it reads as the same object as the addresses on the About page. */
.write-to { margin-top: 1.5rem; max-width: 34rem; }
.write-to > div { padding: .85rem 0; }
.write-to__addr { font-size: var(--step-1); }
.write-to__plain { font-size: var(--step-0); font-weight: 400; color: var(--text); }
.write-to__list {
  margin: 0;
  padding-left: 1.15rem;
  display: grid;
  gap: .3rem;
  font-size: .94rem;
  line-height: 1.5;
}
.write-to__list li::marker { color: var(--alpenglow); }

/* The one restriction on this page that decides whether any of it applies to
   you. Louder than a .note on purpose: it was a sentence inside a paragraph,
   which is exactly where a scanning reader misses it. */
.gate {
  padding: 1.15rem 1.35rem;
  border-left: 4px solid var(--alpenglow);
  background: var(--alpenglow-tint);
  border-radius: 0 var(--r) var(--r) 0;
}
.gate__headline {
  font-size: var(--step-1);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
.gate__detail {
  margin-top: .5rem;
  font-size: .94rem;
  line-height: 1.55;
  color: var(--text);
}

.note {
  display: flex;
  gap: .7rem;
  padding: 1rem 1.15rem;
  background: var(--alpenglow-tint);
  border-radius: var(--r);
  font-size: .92rem;
  line-height: 1.55;
}
.note .icon { color: var(--accent-text); margin-top: .15em; }
.note a { color: var(--accent-text); }


/* ==========================================================================
   11. JOIN STEPS
   ========================================================================== */

/* Two columns. The two mailing-list cards have to land side by side on the
   same row, or a reader who is not at Caltech reads only the first one. */
.steps { display: grid; gap: 1rem; }
@media (min-width: 760px) {
  .steps { grid-template-columns: repeat(2, 1fr); }
}

.step {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(1.5rem, 3vw, 2rem);
  background: var(--white);
  border-radius: var(--r);
  box-shadow: var(--shadow-card);
}
/* .step__num, .step__who and .step__same are gone with the 1a/1b/2/3
   numbering: joining is one email, and numbering Slack as step 2 drew a
   workflow diagram over it. */
.step--primary { box-shadow: 0 0 0 2px var(--alpenglow), var(--shadow-card); }
.step h2 { font-size: var(--step-2); margin-bottom: .6rem; }
.step p { color: var(--text-mute); line-height: 1.6; }
.step > p + p { margin-top: .7rem; }
.step__hint { font-size: .84rem; }

/* The buttons must line up across the cards, whatever the text above them
   does. That means the action block holds ONLY the button and is pushed to the
   bottom; explanatory notes flow with the paragraphs above it. */
.step__action { margin-top: auto; padding-top: 1.5rem; }

.faq { display: grid; gap: 0; }
.faq__item { border-top: 1px solid var(--line); }
.faq__item:last-child { border-bottom: 1px solid var(--line); }
.faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  padding: 1.15rem 0;
  background: none;
  border: 0;
  text-align: left;
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.015em;
  cursor: pointer;
}
.faq__q .icon { color: var(--text-mute); transition: transform var(--speed) var(--ease); }
.faq__q[aria-expanded="true"] .icon { transform: rotate(180deg); }
.faq__a { padding-bottom: 1.35rem; max-width: 62ch; color: var(--text-mute); }
.faq__a[hidden] { display: none; }
.faq__a > * + * { margin-top: .8em; }
.faq__a a { color: var(--accent-text); }


/* ==========================================================================
   12. FOOTER
   ========================================================================== */

.site-footer {
  position: relative;
  margin-top: auto;
  background: var(--ink);
  color: var(--on-dark-mute);
  overflow: hidden;
  padding-top: clamp(3rem, 7vw, 5rem);
}
.site-footer__topo {
  position: absolute;
  inset: 0;
  pointer-events: none;
  display: none;
}
@supports ((-webkit-mask-image: url('')) or (mask-image: url(''))) {
  .site-footer__topo {
    display: block;
    background-color: #fff;
    opacity: .04;
    -webkit-mask: var(--topo-image) center / cover no-repeat;
            mask: var(--topo-image) center / cover no-repeat;
  }
}

.site-footer__inner {
  position: relative;
  display: grid;
  gap: clamp(2.25rem, 5vw, 4rem);
  padding-bottom: 3rem;
}
@media (min-width: 900px) {
  /* 20rem, not 22rem. The footer nav column has to hold one link column per
     nav item, and a fifth item (Get Involved) did not fit in what was left,
     so it wrapped alone onto a second row. Two rem off the brand blurb is
     cheaper than shrinking the headings until they wrap. */
  .site-footer__inner { grid-template-columns: 20rem 1fr; }
}

/* The logo is a wide lockup, not a square mark, so it is sized by HEIGHT and
   the width follows the artwork. Fixing both would squash it. */
.site-footer__mark { height: 2.5rem; width: auto; margin-bottom: 1rem; }
.site-footer__blurb { font-size: .94rem; line-height: 1.65; max-width: 34ch; }

/* Contact details, spelled out. The footer is on every page, so this is where
   somebody looks when they want to reach a human. */
.site-footer__contact { margin-top: 1.75rem; }
.site-footer__contact dt {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--on-dark-mute);
  margin-bottom: .25rem;
}
.site-footer__contact dd {
  margin: 0 0 .9rem;
  font-size: .95rem;
}
.site-footer__contact a {
  color: var(--white);
  text-decoration: none;
  border-bottom: 1px solid #46504f;
  word-break: break-all;
}
.site-footer__contact a:hover { border-bottom-color: var(--accent-on-dark); color: var(--accent-on-dark); }
.site-footer__contact .note-small {
  display: block;
  font-size: .82rem;
  color: var(--on-dark-mute);
  margin-top: .15rem;
}

/* ONE ROW OF PAGES. This was a four-column grid of every section anchor on
   the site, repeated on all eight pages. Six links wrap on a phone and sit on
   one line on a laptop. */
.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem 1.5rem;
}
.site-footer__links a {
  font-size: .9rem;
  text-decoration: none;
  color: var(--on-dark-mute);
}
.site-footer__links a:hover { color: var(--white); }

.site-footer__legal {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  justify-content: space-between;
  padding-block: 1.5rem;
  border-top: 1px solid var(--ink-line);
  font-size: .84rem;
}
.site-footer__legal ul { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; }
.site-footer__legal a { color: var(--on-dark-mute); text-decoration: none; }
.site-footer__legal a:hover { color: var(--white); text-decoration: underline; }

/* Keep the footer at the bottom on short pages. */
body { min-height: 100vh; display: flex; flex-direction: column; }
main  { flex: 1 0 auto; }


/* ==========================================================================
   13. UTILITIES, MOTION, PRINT
   ========================================================================== */

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

.center { text-align: center; }
.center .lede, .center .section-head__text { margin-inline: auto; }
.center .btn-row { justify-content: center; }

.stack    > * + * { margin-top: 1.35rem; }
.stack-lg > * + * { margin-top: 2.5rem; }

.mt-lg { margin-top: clamp(2rem, 5vw, 3.5rem); }

/* --------------------------------------------------------------------------
   Touch targets.

   On a mouse a 22px-tall "Details" link is fine; on a phone it is a miss
   waiting to happen. Rather than inflate everything for everyone, grow the
   small controls only where the pointer is coarse — measured at 375px, the
   offenders were the footer nav (16px), the Details buttons (22px) and the
   copyable addresses (24px).
   -------------------------------------------------------------------------- */
@media (pointer: coarse) {
  .site-footer__links a,
  .site-footer__legal a,
  .site-footer__contact a { display: inline-block; padding-block: .68rem; }

  .event-card__details,
  .event-row__details,
  .event-card__cal,
  .arrow-link { padding-block: .6rem; }

  .copy { padding-block: .5rem; }

  .mobile-nav__sub a { padding: .6rem .85rem; }
}

/* Anyone who has asked their system to calm down gets a calm site. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .masthead, .mobile-nav, .site-footer, .calendar-embed, .skip-link { display: none !important; }
  body { background: #fff; color: #000; }
  .section--dark, .hero, .page-hero { background: #fff !important; color: #000 !important; }
  .section--dark h1, .section--dark h2, .hero__title, .page-hero h1 { color: #000 !important; }
  .topo, .hero__topo, .hero__media { display: none !important; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .8em; }
}


/* ==========================================================================
   14. ROLES, VACANCIES, AND PHOTOGRAPHIC PAGE HEROES

   Added August 2026, in response to two pieces of feedback: that open roles
   were impossible to find, and that the site looked generated. The second one
   is mostly answered here, by two changes with no new components:

     * a page hero can carry a PHOTOGRAPH. Every inner page used to open with
       the same dark block and the same contour pattern, which is what made six
       different pages read as one template. The contour map is a genuine survey
       of Mount Wilson and it earns its place once, not six times, so it is now
       the fallback rather than the default.

     * nothing below introduces a card. The homepage notice is a line on a
       rule; the invitation on the About page is a band between two rules.
       Reaching for another rounded rectangle is exactly the reflex that
       produced the complaint.

   REVISED AUGUST 2026, after the first answer to "open roles are impossible to
   find" turned out to have its own cost. An empty role was rendered as a card
   the same size and shape as a person's, inside the roster grid, with "Could be
   you" where the name goes. It was findable. It also meant the one grid on the
   site that answers "who runs the club" contained things that are not people
   and cannot be written to, so a reader had to check each card to learn which
   kind it was. Actual people look like people; an opening is not a person and
   does not borrow their shape. The dashed placeholder frame is gone, and the
   invitation is .join-callout below: one band, under the roster, once.
   ========================================================================== */

/* --- photographic page hero --- */
.page-hero--photo { padding-block: clamp(4rem, 9vw, 7rem); }
.page-hero--sm    { padding-block: clamp(2.5rem, 5vw, 3.75rem); }

.page-hero__media {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center 45%;
}
/* Same scrim as the homepage hero, so a heading is legible over whatever
   photograph an officer drops in without anyone checking the contrast. */
.page-hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, rgba(14,17,19,.88) 0%, rgba(14,17,19,.60) 52%, rgba(14,17,19,.34) 100%),
    linear-gradient(to top,   rgba(14,17,19,.60) 0%, rgba(14,17,19,0) 60%);
}

/* Where the photo was taken. Small, in the corner, and skipped entirely when
   the CSV has nothing to say about it. */
.page-hero__credit {
  position: absolute;
  right: var(--gutter);
  bottom: .6rem;
  margin: 0;
  font-size: .74rem;
  letter-spacing: .04em;
  color: rgba(255, 255, 255, .55);
}
@media (max-width: 640px) { .page-hero__credit { display: none; } }


/* --- the homepage invitation ---------------------------------------------
   A heading and a sentence between the hero and the events. Rules above and
   below rather than a filled box: it should read as the club saying something,
   not as an advertisement placed on the page.                               */
.wanted-strip {
  border-block: 1px solid var(--line-strong);
  background: var(--paper-2);
}
.wanted-strip__inner {
  display: flex;
  flex-wrap: wrap;
  /* Centred, not baseline: the text block is two lines now, and a baseline
     would pin the link beside the heading rather than beside the block. */
  align-items: center;
  justify-content: space-between;
  gap: .6rem 1.5rem;
  padding-block: 1.05rem;
}
.wanted-strip__text {
  font-size: var(--step-0);
  max-width: 60ch;
}
.wanted-strip__text p { margin: 0; }
.wanted-strip__title {
  font-weight: 700;
  margin-bottom: .15rem !important;
}
/* Who the officer positions are for. One line, in the muted colour, so it
   reads as a condition on the heading above rather than as a second heading or
   as a claim about who may join the club. */
.wanted-strip__who {
  color: var(--text-mute);
  margin-bottom: .55rem !important;
}
/* The openings: one per line, name then seat count.
   ---------------------------------------------------------------------------
   ONE PER LINE, not inline. They were laid out inline while they were bare
   names, and that was right for names; each now carries a count, and two
   name-plus-count pairs on one line make the reader work out which number
   belongs to which title. Stacked, the titles form a column to scan and the
   counts form a second one.

   The count is NOT the call to action. It is set smaller, in the muted colour,
   and it does not take the link -- the title is the only thing here worth
   clicking, and "0/1 filled" as a link target would be a very strange thing to
   click. list-style is off because a bullet adds a third mark to a line that
   already has two things on it; the <ul> is there so a screen reader is told
   how many there are. */
.wanted-strip__roles {
  list-style: none;
  margin: 0;
  padding: 0;
  /* TWO COLUMNS, so the counts line up under each other. Laid out as a flex
     row per item, "0/1 filled" starts wherever the title happens to end, and
     with four openings of different name lengths the counts scatter across the
     block -- which is the opposite of scannable. `display:contents` on the <li>
     hands the title and the count straight to this grid while keeping the list
     a real list for a screen reader. max-content twice, so the columns are as
     wide as their longest title and no wider. */
  display: grid;
  grid-template-columns: max-content max-content;
  gap: .3rem .9rem;
  align-items: baseline;
}
.wanted-strip__role { display: contents; }
.wanted-strip__count {
  font-size: .84rem;
  color: var(--text-mute);
  white-space: nowrap;
}
/* On a narrow screen two columns of a long title plus a count do not fit, so
   the count goes under its title and the grid becomes one column. */
@media (max-width: 560px) {
  .wanted-strip__roles { grid-template-columns: 1fr; gap: .1rem; }
  .wanted-strip__count { margin-bottom: .35rem; }
}
.wanted-strip__text a {
  font-weight: 600;
  color: var(--accent-text);
  text-decoration: underline;
  text-underline-offset: .18em;
  text-decoration-thickness: 1.5px;
}
.wanted-strip__text a:hover { color: var(--alpenglow-hover); }


/* --- the invitation under the roster -------------------------------------
   One band, under the officers, and only when the club is actually asking for
   somebody. A band between two rules rather than a box: it is the club saying
   something at the end of the roster, not an advertisement dropped on the page,
   and it has to sit next to a grid of photographs without competing with them.

   It is NOT in the grid. That was the previous answer and it cost more than it
   bought -- see the note at the top of this section.                          */
.join-callout {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 2rem;
  padding-block: 1.4rem;
  border-block: 1px solid var(--line-strong);
}
.join-callout__text { max-width: 62ch; }
.join-callout__title {
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.015em;
  margin-bottom: .35rem;
}
.join-callout p {
  margin: 0;
  color: var(--text-mute);
  line-height: 1.6;
}
/* The button must not wrap to a second line and then sit half a screen from
   the sentence it belongs to. */
.join-callout .btn { flex: 0 0 auto; }


/* --- what the club needs, on the Get Involved page -----------------------
   Two columns on one rule per row: who, and how it stands. The status used to
   sit UNDER the role name as a sentence, which meant the reader had to read
   each row to compare two of them. Putting it on the right, right-aligned,
   makes the column of statuses readable straight down -- which is the whole
   question somebody has when they arrive at this heading.

   It is not a table. Three fields where one is optional and one is a link is a
   list, and a real <table> would need a header row to be honest, which is a
   row of furniture over three lines of content.                             */
.wanted { list-style: none; margin: 0; padding: 0; }
.wanted__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .1rem 1.5rem;
  padding-block: .8rem;
  border-bottom: 1px solid var(--line);
}
.wanted__item:first-child { border-top: 1px solid var(--line); }
.wanted__role {
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--accent-text);
}
.wanted__role:hover { color: var(--alpenglow-hover); }
/* Who is in it, under the title. Present only when somebody is. */
.wanted__holders {
  display: block;
  margin-top: .1rem;
  font-size: .88rem;
  color: var(--text-mute);
}
/* Quiet on purpose: no badge, no colour coding, no bar. It is two words of
   fact beside a name, and the eye should reach the name first. */
.wanted__note {
  font-size: .88rem;
  color: var(--text-mute);
  white-space: nowrap;
}
/* On a narrow screen the two halves stack, and a right-aligned status would
   hang off the wrong edge under the name it belongs to. */
@media (max-width: 460px) {
  .wanted__item { display: block; }
  .wanted__note { display: block; margin-top: .25rem; }
}


/* --- the role list -------------------------------------------------------
   A definition list in all but name, set two-up. No borders, no backgrounds:
   the roles differ in length, so letting them differ in height is what stops
   ten of them reading as ten identical tiles.                               */
.roles {
  display: grid;
  gap: 2rem 3rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  align-items: start;
}
.role { scroll-margin-top: 7rem; }
.role__name {
  margin: 0;
  font-size: var(--step-1);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
}
/* The one place a role is marked out, and it is a rule rather than a box. */
.role--wanted {
  border-left: 3px solid var(--alpenglow);
  padding-left: 1.1rem;
  margin-left: -1.1rem;
}
.role__what {
  margin: .5rem 0 0;
  max-width: 52ch;
  line-height: 1.6;
}
.role__who {
  margin: .55rem 0 0;
  font-size: .89rem;
  color: var(--text-mute);
}
.role__status {
  display: block;
  color: var(--accent-text);
  font-weight: 600;
}
.role__how {
  margin: .3rem 0 0;
  font-size: .82rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-mute);
}
