/* ==========================================================================
   NUSCM Design System — 2025/26 brand
   Single shared stylesheet for every page. Edit here, not in page <style> tags.

   Contents
     1.  Tokens          8.  Buttons
     2.  Reset           9.  Cards
     3.  Typography      10. Marquee
     4.  Layout          11. Stats
     5.  Motifs          12. Gallery
     6.  Header / nav    13. Timeline
     7.  Hero            14. Forms
                         15. Footer
                         16. Motion & a11y
   ========================================================================== */

/* 1. TOKENS ================================================================
   Every colour below is taken verbatim from the 2026-27 BRANDING GUIDELINES
   deck. That deck lists 25 swatches -- a dump of everything ever used, not a
   working palette -- so this is a deliberate subset, chosen by sampling how
   colours are actually applied across the LOGOS, INSTAGRAM, LINKEDIN and
   EXTERNAL MATERIALS slides. See REVIEW.md for the full rationale.

   The signature move in the deck is a gradient running
        navy -> forest -> plum
   which appears on the slide backgrounds, the logo tiles and the business
   cards. --grad-brand reproduces it. */
:root {
  /* ---- Core: the gradient axis -------------------------------------- */
  --navy:          #001f3f;   /* deepest ground */
  --forest:        #003f36;   /* the anchor colour, and the solid logo tile */
  --plum:          #8e4585;   /* signature accent; the end of the gradient */

  /* Tints/shades derived from the three above for hovers and layering. */
  --navy-deep:     #001630;
  --forest-deep:   #002b25;
  --forest-soft:   #0d5347;
  --plum-deep:     #74376d;
  --plum-soft:     #a35c9a;
  --teal-mid:      #184048;   /* sampled from the deck's gradients */

  /* ---- Light surfaces ------------------------------------------------ */
  --white:         #ffffff;   /* the page ground */
  --surface:       #ffffff;   /* cards -- separated from the page by a hairline */
  --mist:          #f6f8f7;   /* alternating band: 8% sage on white, cool not warm */

  /* ---- Neutrals (official swatches, not invented) --------------------- */
  --charcoal:      #343a40;   /* body copy on light */
  --grey:          #666a6d;   /* secondary copy on light */
  --sage:          #8ca396;   /* muted accent, hairlines */

  /* ---- Supporting ----------------------------------------------------- */
  /* Gold is an official swatch but is deliberately UNUSED: it is a tan, and
     the brief is a white-and-gradient palette with no warm neutrals. Left
     defined so it is one line to reintroduce. */
  --gold:          #c2b280;
  --maroon:        #6a171f;   /* error states only */

  /* ---- Semantic text roles -------------------------------------------- */
  --ink:           var(--charcoal);
  --ink-soft:      var(--grey);
  --on-dark:       #ffffff;
  --on-dark-dim:   rgba(255, 255, 255, 0.78);
  --on-dark-faint: rgba(255, 255, 255, 0.62);
  /* kept as aliases so existing rules keep working */
  --on-forest:       var(--on-dark);
  --on-forest-dim:   var(--on-dark-dim);
  --on-forest-faint: var(--on-dark-faint);

  /* ---- The brand gradient --------------------------------------------- */
  --grad-brand: linear-gradient(135deg,
      var(--navy) 0%, #012f3a 28%, var(--forest) 55%, #3c4a5e 80%, var(--plum) 100%);
  --grad-brand-soft: linear-gradient(135deg,
      var(--navy) 0%, var(--forest) 60%, var(--plum-deep) 100%);

  /* ---- Hairlines ------------------------------------------------------- */
  --rule:          rgba(52, 58, 64, 0.16);
  --rule-strong:   rgba(52, 58, 64, 0.30);
  --rule-invert:   rgba(247, 239, 220, 0.20);

  /* ---- Type ------------------------------------------------------------ */
  --font-display: "Fraunces", "Georgia Pro", Georgia, "Times New Roman", serif;
  --font-body:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono:    ui-monospace, "SFMono-Regular", Menlo, monospace;

  /* ---- Spacing scale ---------------------------------------------------- */
  --s-1: 0.25rem;  --s-2: 0.5rem;   --s-3: 0.75rem;  --s-4: 1rem;
  --s-5: 1.5rem;   --s-6: 2rem;     --s-7: 3rem;     --s-8: 4rem;
  --s-9: 6rem;     --s-10: 8rem;

  /* ---- Shell ------------------------------------------------------------ */
  --page-max:   1240px;
  --gutter:     clamp(1.25rem, 5vw, 5rem);
  --radius:     4px;
  --radius-lg:  8px;
  --header-h:   72px;

  --shadow-sm: 0 1px 2px rgba(0, 31, 63, 0.05), 0 2px 8px rgba(0, 31, 63, 0.04);
  --shadow-md: 0 2px 4px rgba(0, 31, 63, 0.06), 0 12px 28px rgba(0, 31, 63, 0.08);
  --shadow-lg: 0 4px 8px rgba(0, 31, 63, 0.07), 0 24px 56px rgba(0, 31, 63, 0.12);

  --ease:   cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur:    260ms;
}

/* 2. RESET ================================================================= */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

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

body {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--ink);
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { list-style: none; }

/* Skip link — keyboard users land here first */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 2000;
  background: var(--forest);
  color: var(--on-forest);
  padding: var(--s-3) var(--s-5);
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; }

/* 3. TYPOGRAPHY ============================================================
   Display face is Fraunces, a variable serif standing in for MIGRA Bold from
   the brand sheet (MIGRA has no open web licence). The SOFT/WONK axes give it
   the same chiselled, slightly quirky editorial feel as the logo wordmark. */
.display,
h1, h2, h3, .h1, .h2, .h3 {
  font-family: var(--font-display);
  font-variation-settings: "SOFT" 0, "WONK" 1;
  font-optical-sizing: auto;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.h-display {                       /* homepage hero only */
  font-size: clamp(2.75rem, 7.5vw, 5.75rem);
  font-weight: 800;
  line-height: 0.98;
  letter-spacing: -0.035em;
}
h1, .h1 { font-size: clamp(2.25rem, 5.5vw, 3.75rem); }
h2, .h2 { font-size: clamp(1.85rem, 4vw, 2.85rem); }
h3, .h3 { font-size: clamp(1.15rem, 2.2vw, 1.45rem); line-height: 1.2; }

h4, .h4 {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Small caps label used above section titles */
.eyebrow {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--grey);
  display: flex;
  align-items: center;
  gap: var(--s-3);
}
.eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: currentColor;
  opacity: 0.55;
  flex: none;
}
.eyebrow.is-centered { justify-content: center; }
.eyebrow.on-dark { color: var(--on-dark); }
/* ...but the gradient sweeps through plum, where gold falls to 2.99:1. Any
   accent sitting on the gradient must be white, which holds 6.29:1 even at
   the plum end. */
.band-gradient .eyebrow.on-dark,
.hero .eyebrow.on-dark,
.page-hero .eyebrow.on-dark { color: var(--on-dark); }

.lede {
  font-size: clamp(1.05rem, 1.6vw, 1.2rem);
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 62ch;
}
.lede.on-dark { color: var(--on-forest-dim); }

.text-muted { color: var(--ink-soft); }
.serif-accent { font-family: var(--font-display); font-style: italic; }

/* 4. LAYOUT ================================================================ */
.wrap {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap-narrow { max-width: 820px; }

.section { padding-block: clamp(3.5rem, 8vw, 6.5rem); }
.section-tight { padding-block: clamp(2.5rem, 5vw, 4rem); }

.section-head { margin-bottom: clamp(2rem, 4vw, 3.25rem); display: grid; gap: var(--s-4); }
.section-head.is-centered { justify-items: center; text-align: center; }
.section-head.is-centered .lede { text-align: center; }

/* Dark band — used for hero, footer, and one or two feature strips */
.band-forest {
  background: var(--forest);
  color: var(--on-forest);
}
/* The deck's signature: navy -> forest -> plum. Used for the hero, the join
   band and the footer, so the gradient bookends every page. */
.band-gradient {
  background: var(--grad-brand);
  color: var(--on-dark);
}
.band-gradient h1, .band-gradient h2, .band-gradient h3 { color: var(--on-dark); }
.band-forest h1, .band-forest h2, .band-forest h3 { color: var(--on-forest); }
.band-cream { background: var(--mist); }
.band-surface { background: var(--mist); }

.grid { display: grid; gap: clamp(1rem, 2vw, 1.75rem); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-auto { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

@media (max-width: 900px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  /* Person tiles are small and self-contained; stacking 17 of them one per row
     made the team page an 8,300px scroll. Keep two up on phones. */
  .grid-3:has(.person), .grid-4:has(.person) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 5. MOTIFS ================================================================
   The brand mark is a wireframe globe crossed by an orbit. These two motifs
   echo it: a dotted "shipping route" rule, and a faint meridian grid. */
.route-rule {
  height: 1px;
  border: 0;
  background-image: linear-gradient(to right, var(--rule-strong) 50%, transparent 50%);
  background-size: 8px 1px;
  background-repeat: repeat-x;
  opacity: 0.9;
}
.route-rule.on-dark { background-image: linear-gradient(to right, var(--rule-invert) 50%, transparent 50%); }

/* Faint lat/long grid for dark bands */
.meridian-bg { position: relative; isolation: isolate; }
.meridian-bg::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(var(--rule-invert) 1px, transparent 1px),
    linear-gradient(90deg, var(--rule-invert) 1px, transparent 1px);
  background-size: 88px 88px;
  opacity: 0.28;
  mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 30%, transparent 78%);
}

/* The orbiting globe used in the homepage hero */
.globe {
  width: min(38vw, 460px);
  aspect-ratio: 1;
  color: var(--sage);
  opacity: 0.42;
}
.globe .globe-orbit {
  transform-origin: 50% 50%;
  animation: orbit 42s linear infinite;
}
/* NUSCM across the globe's equator, mirroring the brand mark. The wireframe
   stays faint; the wordmark is brought up so it reads as the logo rather than
   as texture -- but still sits behind the headline, so it is decorative and
   the parent <svg> keeps aria-hidden. */
.globe .globe-word {
  font-family: var(--font-display);
  font-variation-settings: "SOFT" 0, "WONK" 1;
  font-weight: 800;
  font-size: 78px;          /* spans ~265 of the globe's 300 units */
  letter-spacing: -0.01em;
  fill: var(--on-dark);
  opacity: 0.5;
}
@keyframes orbit { to { transform: rotate(360deg); } }

/* 6. HEADER / NAV ========================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 900;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.site-header.is-stuck {
  border-bottom-color: var(--rule);
  background: rgba(255, 255, 255, 0.96);
}
.site-header .wrap {
  display: flex;
  align-items: center;
  gap: var(--s-6);
  min-height: var(--header-h);
}

.brand { display: flex; align-items: center; text-decoration: none; flex: none; }
.brand img { height: 46px; width: auto; }
@media (max-width: 480px) { .brand img { height: 38px; } }

.nav { display: flex; align-items: center; gap: clamp(1rem, 2.2vw, 2.25rem); margin-left: auto; }
.nav a:not(.btn) {
  position: relative;
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink);
  padding-block: var(--s-2);
  white-space: nowrap;
  transition: color var(--dur) var(--ease);
}
.nav a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--forest);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--dur) var(--ease);
}
.nav a:hover { color: var(--forest); }
.nav a:hover::after { transform: scaleX(1); transform-origin: left; }
.nav a[aria-current="page"] { color: var(--forest); font-weight: 600; }
.nav a[aria-current="page"]::after { transform: scaleX(1); background: var(--plum); height: 2px; }

.header-actions { display: flex; align-items: center; gap: var(--s-3); flex: none; }

.icon-link {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--forest);
  border: 1px solid var(--rule);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.icon-link:hover { background: var(--forest); color: var(--white); border-color: var(--forest); }
.icon-link svg { width: 16px; height: 16px; }

.nav-toggle {
  display: none;
  width: 40px; height: 40px;
  place-items: center;
  background: none;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--forest);
}
.nav-toggle svg { width: 18px; height: 18px; }
.nav-toggle .icon-close { display: none; }
.nav-toggle[aria-expanded="true"] .icon-close { display: block; }
.nav-toggle[aria-expanded="true"] .icon-open { display: none; }

@media (max-width: 940px) {
  .nav-toggle { display: grid; }
  .header-actions .icon-link { display: none; }
  .nav {
    position: fixed;
    inset: var(--header-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--forest);
    padding: var(--s-4) var(--gutter) var(--s-6);
    transform: translateY(-120%);
    /* visibility (not just transform) takes the closed drawer's links out of
       the tab order and the accessibility tree. Delayed on close so the slide
       still plays; instant on open. */
    visibility: hidden;
    transition: transform 320ms var(--ease), visibility 0s linear 320ms;
    box-shadow: var(--shadow-lg);
    max-height: calc(100dvh - var(--header-h));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav.is-open {
    transform: translateY(0);
    visibility: visible;
    transition: transform 320ms var(--ease), visibility 0s;
  }
  .nav a:not(.btn) {
    color: var(--on-forest);
    font-size: 1.05rem;
    padding: var(--s-4) 0;
    border-bottom: 1px solid var(--rule-invert);
  }
  .nav a::after { display: none; }
  .nav a[aria-current="page"] { color: var(--on-dark); }
  .nav .btn { margin-top: var(--s-5); justify-content: center; }
}

/* 7. HERO ================================================================== */
.hero {
  position: relative;
  overflow: hidden;
  background: var(--grad-brand);
  color: var(--on-dark);
  /* Asymmetric: the hero is followed by the partner marquee on the same forest
     ground, so a full-size bottom pad reads as dead space rather than breathing room. */
  padding-block: clamp(3.5rem, 9vw, 7rem) clamp(2.5rem, 5vw, 3.5rem);
}
/* The globe is positioned against THIS, not against .hero -- the hero also
   contains the partner strip, and centring on the whole thing pushed the globe
   down until it grazed the divider. */
.hero-stage { position: relative; }

.hero-inner {
  position: relative;
  z-index: 2;
  display: grid;
  gap: var(--s-6);
  justify-items: start;
  max-width: 46rem;
}
/* The headline wraps tighter than the paragraph beneath it — that contrast
   between a short, heavy display line and a longer measure is the whole look. */
.hero .h-display { color: var(--on-forest); max-width: 15ch; }
.hero .hero-lede { max-width: 52ch; color: var(--on-forest-dim); font-size: clamp(1.05rem, 1.7vw, 1.25rem); }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-top: var(--s-2); }

/* The partner strip lives inside the hero so the gradient is continuous. */
.hero-partners {
  position: relative;
  z-index: 2;
  margin-top: clamp(2.5rem, 6vw, 4.5rem);
  padding-top: clamp(1.5rem, 3vw, 2.25rem);
  border-top: 1px solid var(--rule-invert);
}
.hero-partners .eyebrow { margin-bottom: var(--s-5); }

/* Globe sits behind the text, bleeding off the right edge */
.hero-globe {
  position: absolute;
  top: 50%;
  /* Sits at the page-margin edge rather than the content edge, so it clears
     the copy on the left instead of crowding it. */
  right: 0;
  translate: 0 -50%;
  z-index: 1;
  pointer-events: none;
}
@media (max-width: 1024px) {
  .hero-inner { max-width: none; }
  /* Below this the copy needs the full width, so the globe drops behind it and
     the wordmark is dimmed rather than competing with the headline. */
  .hero-globe { right: -18%; opacity: 0.45; }
  .globe .globe-word { opacity: 0.3; }
}
@media (max-width: 700px) {
  .hero-globe { display: none; }
}

/* Compact hero for interior pages */
.page-hero {
  background: var(--grad-brand);
  color: var(--on-dark);
  padding-block: clamp(2.75rem, 6vw, 4.5rem);
  position: relative;
  overflow: hidden;
}
.page-hero .wrap { position: relative; z-index: 2; display: grid; gap: var(--s-4); justify-items: start; }
.page-hero h1 { color: var(--on-forest); max-width: 20ch; }
.page-hero .lede { color: var(--on-forest-dim); max-width: 58ch; }

/* Breadcrumb-ish back link */
.back-link {
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-forest-faint);
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}
.back-link:hover { color: var(--on-dark); }

/* 8. BUTTONS =============================================================== */
.btn {
  --btn-bg: var(--plum);
  --btn-fg: var(--white);
  --btn-bd: var(--plum);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 46px;
  padding: 0.7rem 1.5rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-bd);
  border-radius: var(--radius);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn:hover { --btn-bg: var(--plum-deep); --btn-bd: var(--plum-deep); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn-outline { --btn-bg: transparent; --btn-fg: var(--forest); --btn-bd: var(--rule-strong); }
.btn-outline:hover { --btn-bg: var(--forest); --btn-fg: var(--white); --btn-bd: var(--forest); }

.btn-outline-light { --btn-bg: transparent; --btn-fg: var(--on-forest); --btn-bd: var(--rule-invert); }
.btn-outline-light:hover { --btn-bg: var(--white); --btn-fg: var(--forest); --btn-bd: var(--white); }

.btn-solid { --btn-bg: var(--forest); --btn-fg: var(--white); --btn-bd: var(--forest); }
.btn-solid:hover { --btn-bg: var(--forest-soft); --btn-bd: var(--forest-soft); }

.btn-sm { min-height: 38px; padding: 0.4rem 1rem; font-size: 0.8rem; }
.btn svg { width: 15px; height: 15px; }

/* Text link with arrow */
.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--forest);
  text-decoration: none;
}
.arrow-link svg { width: 14px; height: 14px; transition: transform var(--dur) var(--ease); }
.arrow-link:hover svg { transform: translateX(4px); }
.arrow-link.on-dark { color: var(--on-dark); }

/* 9. CARDS ================================================================= */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: clamp(1.4rem, 2.4vw, 2rem);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
a.card:hover, .card.is-interactive:hover {
  border-color: var(--rule-strong);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.card p { color: var(--ink-soft); font-size: 0.94rem; }

/* Numbered pillar card — the index of what the club offers */
.card-num {
  font-family: var(--font-display);
  font-variation-settings: "SOFT" 0, "WONK" 1;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--plum);
  letter-spacing: 0.06em;
}
.card-title { font-family: var(--font-display); font-size: 1.3rem; font-weight: 700; letter-spacing: -0.015em; }
.card-foot { margin-top: auto; padding-top: var(--s-3); }

/* Person card. Now a link to that person's LinkedIn, so it needs real
   affordance and focus handling as well as the photo treatment. */
.person {
  position: relative;
  display: grid;
  gap: var(--s-3);
  text-align: left;
  align-content: start;
  text-decoration: none;
  color: inherit;
}
a.person:hover .person-name { color: var(--plum); }
a.person:hover .person-photo { filter: saturate(1.05); }
a.person:focus-visible { outline: 2px solid var(--plum); outline-offset: 6px; border-radius: var(--radius); }

/* Small LinkedIn chip, bottom-right of the photo, so it is obvious the card
   goes somewhere. */
.person-photo { position: relative; }
.person-li {
  position: absolute;
  right: var(--s-2);
  bottom: var(--s-2);
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  color: var(--forest);
  opacity: 0;
  translate: 0 4px;
  transition: opacity var(--dur) var(--ease), translate var(--dur) var(--ease);
}
.person-li svg { width: 13px; height: 13px; }
a.person:hover .person-li, a.person:focus-visible .person-li { opacity: 1; translate: 0 0; }

/* On the programme pages the card links to LinkedIn and the email sits beneath
   it as its own link -- nesting one interactive element inside another would be
   both invalid and unusable with a keyboard. */
.person-wrap { display: grid; gap: var(--s-2); align-content: start; }
.person-email {
  font-size: 0.8rem;
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  justify-self: start;
  word-break: break-all;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.person-email:hover { color: var(--plum); border-color: var(--plum); }

/* Fallback for members without a photo yet: initials on the brand gradient. */
.person-monogram {
  width: 100%;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  border-radius: var(--radius);
  background: var(--grad-brand-soft);
  color: var(--white);
  font-family: var(--font-display);
  font-variation-settings: "SOFT" 0, "WONK" 1;
  font-weight: 700;
  font-size: clamp(1.6rem, 4vw, 2.2rem);
  letter-spacing: 0.02em;
}
.person img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  object-position: center top;
  border-radius: var(--radius);
  background: var(--sage);
  filter: saturate(0.92);
  transition: filter var(--dur) var(--ease);
}
.person-name { font-family: var(--font-display); font-size: 1.05rem; font-weight: 700; letter-spacing: -0.01em; transition: color var(--dur) var(--ease); }
.person-role { font-size: 0.8rem; font-weight: 600; color: var(--plum); letter-spacing: 0.02em; }
.person-meta {
  font-size: 0.8rem;
  color: var(--ink-soft);
  min-height: 2.4em;   /* one- and two-line majors keep rows level */
}

/* Logo tile (partners / sponsors) */
.logo-tile {
  display: grid;
  place-items: center;
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  min-height: 96px;
}
.logo-tile img {
  width: var(--w, 96px);
  height: auto;
  max-width: 78%;
  opacity: 0.8;
  filter: grayscale(1);
  transition: opacity var(--dur) var(--ease), filter var(--dur) var(--ease);
}
.logo-tile:hover img { opacity: 1; filter: grayscale(0); }

/* 10. MARQUEE ==============================================================
   Two rows scrolling in opposite directions, borrowed in spirit from the
   reference site. Content is duplicated in markup so the loop is seamless. */
.marquee {
  position: relative;
  overflow: hidden;
  padding-block: var(--s-5);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee-track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: marquee 38s linear infinite;
}
/* Spacing lives on the items, not as a flex `gap`. A gap is only applied
   BETWEEN children, so the duplicated half had no trailing space and the
   translateX(-50%) loop jumped by one gap width on every cycle. */
.marquee-track > * {
  flex: none;
  margin-inline-end: clamp(2.5rem, 6vw, 5rem);
}
.marquee:hover .marquee-track { animation-play-state: paused; }
/* Each logo carries its own width/height attributes, computed so that every
   mark occupies roughly the same AREA (~2500px^2) rather than the same height.
   Fixed-height sizing made a wide wordmark like Tesla read 4.8x larger than a
   squarer mark like Vertex. The source files are 3x for retina and have had
   their transparent padding trimmed -- several shipped with 70-80% padding,
   which is why they used to render tiny. */
.marquee-track img {
  /* --w is set inline per logo: the area-normalised display width. The files
     themselves are 3x that, so they stay sharp on retina. */
  width: var(--w, 96px);
  height: auto;
  max-width: none;
  opacity: 0.78;
  filter: brightness(0) invert(1);
  transition: opacity var(--dur) var(--ease);
}
.marquee-track img:hover { opacity: 1; }
@keyframes marquee { to { transform: translateX(-50%); } }

/* On light backgrounds the logos need the opposite treatment */
.marquee.on-cream .marquee-track img { filter: grayscale(1); opacity: 0.55; }
.marquee.on-cream .marquee-track img:hover { filter: grayscale(0); opacity: 1; }

/* 11. STATS ================================================================ */
.stat-row {
  display: grid;
  /* Fixed 4 -> 2 -> 1 rather than auto-fit: auto-fit produced a 3-column run
     for these 4 items, and the two leftover cells painted as a bare grey-green
     block where the grid gap showed through. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}
.stat {
  background: var(--surface);
  padding: clamp(1.25rem, 3vw, 2rem);
  display: grid;
  gap: var(--s-1);
}
.stat-num {
  font-family: var(--font-display);
  font-variation-settings: "SOFT" 0, "WONK" 1;
  font-size: clamp(2rem, 4.5vw, 3rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--forest);
}
.stat-label {
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
@media (max-width: 760px) { .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 380px) { .stat-row { grid-template-columns: minmax(0, 1fr); } }

/* Inverted variant for dark bands */
.band-gradient .stat-row, .band-forest .stat-row { background: var(--rule-invert); border-color: var(--rule-invert); }
.band-forest .stat { background: var(--forest); }
.band-gradient .stat { background: rgba(0,31,63,0.28); backdrop-filter: blur(2px); }
.band-forest .stat-num { color: var(--on-dark); }
.band-gradient .stat-num { color: var(--on-dark); }
.band-forest .stat-label, .band-gradient .stat-label { color: var(--on-forest-dim); }

/* 12. GALLERY ============================================================== */
.gallery {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 220px;
  gap: var(--s-4);
}
.gallery figure {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--sage);
}
.gallery img { width: 100%; height: 100%; object-fit: cover; transition: transform 600ms var(--ease); }
.gallery figure:hover img { transform: scale(1.04); }

.gallery .span-2 { grid-column: span 2; }
.gallery .row-2 { grid-row: span 2; }

@media (max-width: 900px) {
  .gallery { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 180px; }
}
@media (max-width: 520px) {
  .gallery { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .gallery .span-2 { grid-column: span 1; }
  .gallery .row-2 { grid-row: span 1; }
  /* One per row: let each photo keep a sane shape instead of being cropped
     into a ~2:1 letterbox by a fixed row height. */
  .gallery figure { aspect-ratio: 4 / 3; }
  .gallery figure:has(img[height="1800"]) { aspect-ratio: 3 / 4; }  /* portraits */
}

/* 12b. EVENT LIST ==========================================================
   Populated at runtime by assets/js/events.js from the public Google Calendar. */
.event-list { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden; }
.event-row {
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: var(--s-4);
  align-items: center;
  padding: var(--s-4) var(--s-5);
  background: var(--surface);
  transition: background var(--dur) var(--ease);
}
.event-row:hover { background: var(--mist); }
.event-date {
  display: grid;
  justify-items: center;
  padding: var(--s-2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--white);
}
.event-month {
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--plum);
}
.event-day {
  font-family: var(--font-display);
  font-variation-settings: "SOFT" 0, "WONK" 1;
  font-size: 1.5rem; font-weight: 800; line-height: 1; color: var(--forest);
}
.event-title { font-size: 1.05rem; }
.event-meta { font-size: 0.85rem; color: var(--ink-soft); }
.event-empty { padding: var(--s-6); text-align: center; color: var(--ink-soft); background: var(--surface); font-size: 0.92rem; }

/* Inverted for dark bands */
.band-gradient .event-list, .band-forest .event-list { background: var(--rule-invert); border-color: var(--rule-invert); }
.band-gradient .event-row, .band-forest .event-row { background: var(--forest); }
.band-gradient .event-row, .band-gradient .event-row:hover, .band-forest .event-row:hover { background: var(--forest-soft); }
.band-gradient .event-date, .band-forest .event-date { background: rgba(247,239,220,0.06); border-color: var(--rule-invert); }
.band-gradient .event-day, .band-forest .event-day { color: var(--white); }
.band-gradient .event-meta, .band-forest .event-meta { color: var(--on-forest-dim); }
.band-gradient .event-empty, .band-forest .event-empty { background: var(--forest); color: var(--on-forest-dim); }

/* 13. TIMELINE ============================================================= */
.timeline { display: grid; gap: 0; counter-reset: step; }
.timeline-step {
  position: relative;
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: var(--s-5);
  padding-bottom: var(--s-7);
}
.timeline-step:last-child { padding-bottom: 0; }
.timeline-step::before {           /* the connecting route line */
  content: "";
  position: absolute;
  left: 27px;
  top: 40px;
  bottom: 0;
  width: 1px;
  background-image: linear-gradient(to bottom, var(--rule-strong) 50%, transparent 50%);
  background-size: 1px 7px;
}
.timeline-step:last-child::before { display: none; }
.timeline-marker {
  counter-increment: step;
  width: 56px; height: 40px;
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--forest);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface);
}
.timeline-marker::before { content: "0" counter(step); }
.timeline-body { display: grid; gap: var(--s-2); padding-top: var(--s-2); }

/* 14. FORMS ================================================================ */
.field { display: grid; gap: var(--s-2); }
.field label { font-size: 0.8rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-soft); }
.input, input[type="text"], input[type="email"], select, textarea {
  width: 100%;
  min-height: 46px;
  padding: 0.65rem 0.9rem;
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  font-size: 0.95rem;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.input:focus, input:focus, select:focus, textarea:focus {
  border-color: var(--forest);
  box-shadow: 0 0 0 3px rgba(0, 63, 54, 0.1);
}
/* Keep the visible ring for keyboard users; only suppress it for mouse focus. */
.input:focus:not(:focus-visible),
input:focus:not(:focus-visible),
select:focus:not(:focus-visible),
textarea:focus:not(:focus-visible) { outline: none; }
.inline-form { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.inline-form .input { flex: 1 1 220px; }

/* Dark-band form fields */
.band-gradient .input, .band-forest .input,
.site-footer .input {
  background: rgba(247, 239, 220, 0.08);
  border-color: var(--rule-invert);
  color: var(--on-forest);
}
.band-gradient .input, .band-gradient .input::placeholder, .band-forest .input::placeholder,
.site-footer .input::placeholder { color: rgba(247, 239, 220, 0.66); }
.band-gradient .input, .band-gradient .input:focus, .band-forest .input:focus,
.site-footer .input:focus { border-color: var(--on-dark); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.22); }

.form-note {
  font-size: 0.82rem;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--radius);
  border: 1px solid transparent;
}
/* Light context is the DEFAULT -- these notes appear on white on the check-in
   page. The light-on-dark values are scoped to the dark bands below, where the
   mailing-list notes live. Getting this backwards made every check-in message
   invisible at ~1.15:1. */
.form-note[data-state="success"] { background: rgba(140, 163, 150, 0.20); border-color: var(--sage);  color: var(--forest); }
.form-note[data-state="error"]   { background: rgba(106, 23, 31, 0.10);  border-color: var(--maroon); color: var(--maroon); }

.band-gradient .form-note[data-state="success"], .band-forest .form-note[data-state="success"],
.site-footer .form-note[data-state="success"] { color: var(--on-forest); }
.band-gradient .form-note[data-state="error"], .band-forest .form-note[data-state="error"],
.site-footer .form-note[data-state="error"]   { background: rgba(106, 23, 31, 0.28); color: #ffd9dc; }

/* 15. FOOTER =============================================================== */
.site-footer {
  background: var(--grad-brand-soft);
  color: var(--on-forest);
  padding-block: clamp(3rem, 6vw, 4.5rem) var(--s-6);
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1.4fr;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  padding-bottom: var(--s-7);
}
@media (max-width: 900px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .footer-grid { grid-template-columns: 1fr; } }

.footer-brand { display: grid; gap: var(--s-4); align-content: start; }
.footer-brand img { height: 44px; width: auto; }
.footer-brand p { color: var(--on-forest-dim); font-size: 0.9rem; max-width: 34ch; }

.footer-col h3 {
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--on-dark);
  margin-bottom: var(--s-4);
}
.footer-col ul { display: grid; gap: var(--s-3); }
.footer-col a {
  font-size: 0.9rem;
  color: var(--on-forest-dim);
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}
.footer-col a:hover { color: var(--on-dark); }

.site-footer { --on-dark-faint: rgba(255, 255, 255, 0.66); }  /* 4.55:1 at the footer's plum end */

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4);
  justify-content: space-between;
  align-items: center;
  padding-top: var(--s-5);
  border-top: 1px solid var(--rule-invert);
  font-size: 0.8rem;
  color: var(--on-forest-faint);
}
.footer-bottom a { color: var(--on-forest-faint); text-decoration: none; }
.footer-bottom a:hover { color: var(--on-forest-dim); }
.footer-social { display: flex; gap: var(--s-3); }
.footer-social a {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: 1px solid var(--rule-invert);
  border-radius: 50%;
  color: var(--on-forest-dim);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.footer-social a:hover { background: var(--white); color: var(--navy); border-color: var(--white); }
.footer-social svg { width: 15px; height: 15px; }

/* 16. MOTION & ACCESSIBILITY =============================================== */
/* Scroll reveal. Elements start hidden ONLY when JS is running, so the page
   is fully readable if JS fails — see the .js guard added by nuscm.js. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 620ms var(--ease), transform 620ms var(--ease);
}
.js [data-reveal].is-visible { opacity: 1; transform: none; }

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--plum);
  outline-offset: 3px;
  border-radius: 2px;
}
.band-gradient :focus-visible, .band-forest :focus-visible, .site-footer :focus-visible, .hero :focus-visible { outline-color: var(--on-dark); }

main:focus { outline: none; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

@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;
  }
  .js [data-reveal] { opacity: 1; transform: none; }
}

@media print {
  .site-header, .site-footer, .marquee, .hero-globe { display: none; }
  body { background: #fff; color: #000; }
  /* Scroll-reveal never fires when printing -- the page is rasterised without
     ever being scrolled -- so without this every revealed section prints blank. */
  .js [data-reveal] { opacity: 1 !important; transform: none !important; }
  .band-forest, .hero, .page-hero { background: #fff !important; color: #000 !important; }
  .band-forest h1, .band-forest h2, .band-forest h3,
  .hero .h-display, .page-hero h1 { color: #000 !important; }
  .lede.on-dark, .hero-lede, .page-hero .lede { color: #333 !important; }
  /* These tokens are tuned for a dark ground; on white paper they vanish. */
  .eyebrow, .eyebrow.on-dark, .card-num, .person-role,
  .event-month, .footer-col h3, .stat-num { color: #000 !important; }
  .stat-label, .back-link, .footer-bottom, .footer-col a { color: #333 !important; }
  .btn { background: none !important; color: #000 !important; border-color: #000 !important; }
  .card, .stat, .event-row, .logo-tile { border-color: #999 !important; background: #fff !important; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.85em; word-break: break-all; }
}
