:root {
  --cream: #F1EEE3;
  --ink: #171717;
  --blue: #1D2FA6;
  --coral: #E24A2B;
  --green-light: #6FE05C;
  --green-deep: #00D168;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: 'Source Code Pro', 'IBM Plex Mono', monospace;
  line-height: 1.5;
  /* Set by js/common.js while .sticky-nav is visible, so scrolled content
     is pushed clear of the fixed bar instead of disappearing under it.
     Transition timing matches .sticky-nav's own reveal (0.3s ease) so the
     bar sliding in and the content making room for it read as one motion. */
  padding-top: 0;
  transition: padding-top 0.3s ease;
}

a { color: inherit; }

/* Header */

.site-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 45px 19px 19px;
}

.header-inner {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
}

.brand {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.brand-logo {
  height: 75px;
  width: auto;
  display: block;
}

.site-nav {
  margin-top: 13px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  line-height: 1.15;
  font-size: 1.235rem;
}

.site-nav a {
  color: var(--coral);
  text-decoration: none;
}

.site-nav a:hover,
.site-nav a:focus-visible {
  text-decoration: underline;
}

.sticky-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  background: var(--cream);
  /* Same hairline already used as a section divider elsewhere on the site
     (e.g. rgba(23, 23, 23, 0.15) border-bottom), reused here instead of a
     shadow so the nav separates from the content the same way the rest of
     the page does. */
  border-bottom: 1px solid rgba(23, 23, 23, 0.15);
  transform: translateY(-100%);
  opacity: 0;
  transition: transform 0.3s ease, opacity 0.3s ease;
  pointer-events: none;
}

.sticky-nav.visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

.sticky-nav-inner {
  max-width: 1400px;
  margin: 0 auto;
  padding: 16px 19px 18.4px; /* bottom padding +15% (16px -> 18.4px) to make the bar taller */
  display: flex;
  align-items: center;
  gap: 24px;
}

/* Split the same way as the mobile nav's logo: a text-only raster
   wordmark (figure erased to transparent) plus a live inline SVG figure
   overlaid on the wordmark's own footprint, so the sticky bar's figure
   gets the exact same color and arm-wave animation as the main header's
   logo-animated.svg, instead of a flat, slightly-off-green, static PNG
   figure baked into logo-horizontal.png. */
.sticky-logo-wrap {
  position: relative;
  height: 38.4px; /* 20% up from 32px */
  aspect-ratio: 229 / 49;
}

.sticky-logo {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.sticky-logo-figure {
  position: absolute;
  left: 84.28%;
  top: 0;
  width: 15.72%;
  height: 85.71%;
  display: block;
}

#lone-sticky-mark .lp-mark { fill: var(--green-deep); }

#lone-sticky-mark .lp-arm {
  transform-origin: 448.82px 90.72px;
  animation: lp-nav-wave 2.88s ease-in-out infinite;
}

/* Shared by both the sticky-nav figure above and the mobile-nav figure
   below (see the "Mobile nav" section) — defined at top level, not
   nested in a media query, so it's available to whichever one is
   actually visible. */
@keyframes lp-nav-wave {
  0%, 100% { transform: rotate(-10deg); }
  50% { transform: rotate(28deg); }
}

/* Static shoulder-joint fallback cap, shown only when the wave animation
   is off (see the reduced-motion block below). */
#lone-nav-mark .lp-shoulder-static,
#lone-sticky-mark .lp-shoulder-static {
  display: none;
}

.sticky-links {
  display: flex;
  align-items: center;
  gap: 18px;
  font-size: 0.9rem;
}

.sticky-links a {
  color: var(--coral);
  text-decoration: none;
}

.sticky-links a:hover,
.sticky-links a:focus-visible {
  text-decoration: underline;
}

/* ==========================================================================
   Mobile nav — below 720px (the point where .sticky-nav-inner's single-row
   link list starts running out of room and cropping, same breakpoint
   already used elsewhere on the site), both .site-nav and .sticky-nav are
   replaced by one fixed panel: a 60px cream bar (logo + "···") that
   animates to full height and coral on open, revealing the link list.
   Reviewed and refined in mobile-nav-concept.html before landing here.

   The logo is a raster wordmark (two pre-recolored copies of
   logo-horizontal.png — figure erased to transparent on both — crossfade
   on open) plus a live inline SVG figure positioned over the wordmark's
   own footprint (measured from its pixel bounding box: 84.28%/0%/15.72%/
   85.71% of the lockup), reusing the exact arm-wave keyframes from the
   header's logo-animated.svg.
   ========================================================================== */

.mnav { display: none; }

@media (max-width: 720px) {
  /* .site-header itself (not just .site-nav) is hidden — .mnav replaces
     its job entirely, including the logo, so there's no leftover
     stacked-logo block peeking out above/behind the fixed bar. Content
     gets its clearance back via the body padding below instead of the
     header's natural in-flow height. */
  .site-header,
  .sticky-nav {
    display: none;
  }

  body {
    padding-top: 78px; /* 30% taller than the original 60px, matches .mnav/.mnav-bar below */
  }

  .mnav {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 78px; /* 30% taller than the original 60px */
    /* Matches .sticky-nav's desktop treatment: a solid cream bar with the
       same ink-tinted hairline as a bottom border, instead of the old
       frosted/backdrop-blur look. */
    background: var(--cream);
    border-bottom: 1px solid rgba(23, 23, 23, 0.15);
    overflow: hidden;
    z-index: 60;
    transition: height 0.4s cubic-bezier(.4, 0, .2, 1), background-color 0.4s ease, border-color 0.4s ease;
  }

  .mnav.open {
    border-bottom-color: transparent;
  }

  .mnav.open {
    height: 100%;
    background: var(--coral);
  }

  .mnav-bar {
    flex: 0 0 78px; /* 30% taller than the original 60px */
    height: 78px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 19px;
    box-sizing: border-box;
  }

  .mnav-logo {
    position: relative;
    height: 41.6px; /* 30% larger than the original 32px */
    aspect-ratio: 229 / 49;
  }

  .mnav-logo-img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: block;
    transition: opacity 0.3s ease;
  }

  .mnav-logo-color { opacity: 1; }
  .mnav-logo-white { opacity: 0; }

  .mnav.open .mnav-logo-color { opacity: 0; }
  .mnav.open .mnav-logo-white { opacity: 1; }

  .mnav-logo-figure {
    position: absolute;
    left: 84.28%;
    top: 0;
    width: 15.72%;
    height: 85.71%;
    display: block;
  }

  #lone-nav-mark .lp-mark {
    fill: var(--green-deep);
    transition: fill 0.3s ease;
  }

  .mnav.open #lone-nav-mark .lp-mark { fill: #ffffff; }

  #lone-nav-mark .lp-arm {
    transform-origin: 448.82px 90.72px;
    animation: lp-nav-wave 2.88s ease-in-out infinite;
  }

  .mnav-toggle {
    background: none;
    border: none;
    padding: 10px;
    margin: 0 -10px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .mnav-dots {
    font-family: 'Source Code Pro', monospace;
    font-size: 0.91rem;
    letter-spacing: 2px;
    color: var(--coral);
    transition: color 0.3s ease;
  }

  .mnav.open .mnav-dots { color: #ffffff; }

  .mnav-links {
    display: flex;
    flex-direction: column;
    /* Left inset nudged 20% of the way from the bar's edge toward the
       panel's horizontal center: 20px + 0.2 * (50% - 20px). */
    padding: 8px 20px 40px calc(16px + 10%);
    opacity: 0;
    transform: translateY(6px);
    pointer-events: none;
    transition: opacity 0.25s ease, transform 0.25s ease;
  }

  .mnav.open .mnav-links {
    opacity: 1;
    transform: none;
    pointer-events: auto;
    transition-delay: 0.15s;
  }

  .mnav-links a {
    color: rgba(255, 255, 255, 0.8);
    text-decoration: none;
    font-family: 'Source Code Pro', monospace;
    font-weight: 400;
    font-size: 1.16rem;
    padding: 11px 0;
  }

  .mnav-links a:hover,
  .mnav-links a:focus-visible {
    text-decoration: underline;
  }

  body.mnav-locked {
    overflow: hidden;
  }
}

@media (prefers-reduced-motion: reduce) {
  .mnav,
  .mnav-logo-img,
  .mnav-links,
  .mnav-dots {
    transition: none;
  }

  #lone-nav-mark .lp-arm,
  #lone-sticky-mark .lp-arm {
    animation: none;
  }

  #lone-nav-mark .lp-shoulder-dynamic,
  #lone-sticky-mark .lp-shoulder-dynamic {
    display: none;
  }

  #lone-nav-mark .lp-shoulder-static,
  #lone-sticky-mark .lp-shoulder-static {
    display: block;
  }
}

/* Intro */

.intro {
  max-width: 760px;
  margin: 45px auto 0;
  padding: 0 19px;
}

/* Team page only: the carousel below needs every bit of vertical room it
   can get on an ordinary laptop screen (see .team-gallery--spacious), so
   this heading sits closer to the header here than the 45px every other
   page uses for its own title. */
body.page-team .intro {
  margin-top: 20px;
}

.intro h1 {
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  /* Fixed, not responsive — was clamp(1rem, 3.6vw, 2.15rem), which kept
     shrinking as the window narrowed at every width, not just below a
     breakpoint. One constant size everywhere; only white-space changes
     below 960px (see media query) so long titles can wrap instead of
     overflowing on narrow screens. */
  font-size: 2.55rem;
  color: var(--ink);
  margin: 0 0 18px;
  white-space: nowrap;
  overflow-wrap: normal;
}

.cursor {
  display: inline-block;
  margin-left: 2px;
  animation: blink 1s step-end infinite;
}

@keyframes blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

.reveal-item {
  opacity: 0;
  transition: opacity 0.6s ease;
}

.reveal-item.visible {
  opacity: 1;
}

.no-person-icon {
  width: 52px;
  height: auto;
  display: block;
  margin-bottom: 12px;
}

.lead {
  font-size: 0.95rem;
  max-width: 46em;
  margin: 0 0 24px;
}

.stat {
  color: var(--blue);
  font-size: 1.05rem;
  max-width: 50em;
  margin: 24px 0 0;
}

.stat .figure {
  color: var(--blue);
  font-weight: 600;
  transition: color 0.6s ease;
}

.stat-row.sad .figure {
  color: var(--coral);
}

.landscape-wrap {
  position: relative;
  width: 100%;
  margin-top: 65px;
}

.photo-landscape {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1600 / 556;
  object-fit: cover;
  object-position: center top;
  mix-blend-mode: darken;
}

.stat-wrap {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  margin: 0 auto;
  max-width: 760px;
  padding: 0 19px;
  height: 22%;
  display: flex;
  align-items: flex-end;
  pointer-events: none;
  box-sizing: border-box;
}

.stat-row {
  position: relative;
  width: 100%;
}

.landscape-wrap .stat {
  margin: 0;
  text-align: left;
  width: 100%;
}

.photo-street-wrap {
  position: relative;
  display: block;
  width: 100%;
  max-width: 480px;
}

.mood-emoji {
  position: absolute;
  top: 50%;
  left: calc(100% + 60px);
  transform: translateY(-50%);
  width: 64px;
  height: 64px;
  pointer-events: none;
  color: var(--blue);
  transition: color 0.6s ease;
}

#moodEmoji {
  display: none;
}

.mood-emoji.sad {
  color: var(--coral);
}

.mood-emoji-2 {
  position: absolute;
  top: 50%;
  left: calc(100% + 50px);
  transform: translateY(-50%);
  transition: color 0.6s ease;
}

@media (max-width: 1260px) {
  .mood-emoji-2 {
    display: none;
  }
}

.mood-mouth {
  d: path("M32,64 Q50,80 68,64");
  transition: d 0.6s ease;
}

.mood-emoji.sad .mood-mouth {
  d: path("M32,64 Q50,48 68,64");
}

@media (max-width: 1000px) {
  .mood-emoji {
    display: none;
  }
}

.photo-street {
  display: block;
  width: 100%;
  max-width: 480px;
  aspect-ratio: 3 / 2;
  object-fit: contain;
  mix-blend-mode: darken;
  margin: 0 0 8px;
}

/* Join section */

.join-banner {
  display: block;
  width: 100%;
  background: var(--green-light);
  color: #ffffff;
  padding: 18px 0;
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  font-size: 1.3rem;
  position: relative;
  border: none;
  cursor: pointer;
  text-align: left;
}

.join-banner-inner {
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 19px;
  display: flex;
  align-items: center;
  gap: 12px;
}

.hiker-icon { width: 22px; height: auto; }

.chevron {
  position: absolute;
  bottom: -14px;
  left: 50%;
  transform: translateX(-50%);
  width: 28px;
  height: 28px;
  transition: transform 0.3s ease;
}

.join-banner.open .chevron {
  transform: translateX(-50%) rotate(180deg);
}

.join-content {
  background: var(--green-deep);
  color: #ffffff;
  width: 100%;
  position: relative;
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.4s ease;
}

.join-content.open {
  max-height: 1400px;
}

.join-bg {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 42%;
  object-fit: cover;
  object-position: center 20%;
  mix-blend-mode: darken;
  opacity: 0.8;
  z-index: 0;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 35%);
  mask-image: linear-gradient(to bottom, transparent 0%, black 35%);
}

.join-content-inner {
  position: relative;
  z-index: 1;
  max-width: 1400px;
  margin: 0 auto;
  padding: 45px 19px 58px;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 38px;
}

.steps {
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.step h3 {
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  font-size: 1.05rem;
  margin: 0 0 8px;
}

.step p {
  margin: 0;
  font-size: 0.9rem;
  opacity: 0.95;
}

.signup-form h3 {
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  font-size: 1.05rem;
  margin: 0 0 16px;
}

.field-label {
  display: block;
  font-size: 0.85rem;
  margin: 13px 0 5px;
}

.name-row {
  display: flex;
  gap: 10px;
}

.signup-form input {
  background: transparent;
  border: 1.5px solid #ffffff;
  border-radius: 4px;
  color: #ffffff;
  font-family: 'Source Code Pro', monospace;
  padding: 10px 12px;
  font-size: 0.85rem;
  width: 100%;
}

.signup-form input::placeholder {
  color: rgba(255,255,255,0.75);
}

.signup-form input:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
}

/* Responsive */

@media (max-width: 720px) {
  .join-content-inner {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 960px) {
  /* Font-size is fixed sitewide now (see .intro h1 above), so this only
     switches wrapping: nowrap works on wide screens where the 760px
     .intro column comfortably fits the title on one line, but the same
     fixed size needs to wrap on narrow screens instead of overflowing. */
  .intro h1 {
    white-space: normal;
  }
}

@media (max-width: 480px) {
  .landscape-wrap .stat {
    font-size: 0.85rem;
    line-height: 1.4;
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* Team page */

.banner-full {
  display: block;
  width: 100%;
  height: auto;
}

.dark-section {
  background: #282828;
  color: #F1EEE3;
  padding: 45px 19px 58px;
}

.dark-lead {
  max-width: 760px;
  margin: 0 auto 40px;
  font-size: 0.95rem;
}

.highlight-green {
  color: var(--green-light);
}

.polaroid-gallery {
  position: relative;
  width: 100%;
  max-width: 1400px;
  margin: 0 auto 40px;
  aspect-ratio: 4372 / 2277;
}

.polaroid {
  position: absolute;
  width: auto;
  height: auto;
  max-width: none;
  max-height: none;
  object-fit: cover;
}

.polaroid-alley {
  left: 8.92%;
  width: 21.89%;
  top: 18.09%;
  height: 70.4%;
}

.polaroid-road {
  left: 32.94%;
  width: 18.83%;
  top: 32.63%;
  height: 67.37%;
}

.polaroid-interior {
  left: 57.82%;
  width: 19.74%;
  top: 0;
  height: 68.03%;
}

.polaroid-caption {
  position: absolute;
  left: 63%;
  top: 42%;
  width: 33%;
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.5;
}

.dark-stat {
  max-width: 760px;
  margin: 0 auto;
  font-size: 1.05rem;
}

.dark-stat .figure {
  color: var(--coral);
  font-weight: 600;
}

.repeat-stat-section {
  background: var(--cream);
  padding: 58px 19px;
}

.repeat-stat {
  max-width: 900px;
  margin: 0 auto;
  color: var(--ink);
  font-size: 1.05rem;
}

@media (max-width: 720px) {
  .polaroid-gallery {
    aspect-ratio: auto;
    display: flex;
    flex-direction: column;
    gap: 24px;
    padding-bottom: 60px;
  }
  .polaroid {
    position: static;
    width: 70%;
    max-width: 280px;
    height: auto;
    margin: 0 auto;
  }
  .polaroid-caption {
    position: static;
    width: 100%;
    margin-top: 8px;
  }
}

/* Team gallery (team.html) */

.team-gallery {
  position: relative;
  margin-top: 12px;
}

.team-gallery-sticky {
  min-height: 360px;
  display: flex;
  flex-direction: column;
  /* Top-aligned rather than centered: when the sticky box is taller than
     its content (i.e. there's slack to give), that space should read as
     breathing room below the description, not get split so half of it
     pushes the photo down from the top before you've even scrolled. */
  justify-content: flex-start;
  background: var(--cream);
}

.team-track-viewport {
  width: 100%;
  /* Compact mode's photo height is solved for by team.js (see
     fitViewportHeight) so it can be as large as possible while the
     divider + full info text still fit the viewport — 300px is only the
     pre-JS/no-JS fallback. Spacious mode overrides this with a fixed
     420px below, at higher specificity. */
  height: var(--team-photo-h, 300px);
  overflow-x: auto;
  overflow-y: hidden;
  /* Belt-and-suspenders: our own JS drives scrolling to a centered
     target, but trackpad momentum on some browsers keeps nudging
     scrollLeft natively even after preventDefault, which can leave the
     strip resting between two photos instead of on one. Native snap
     forces the final rest position back onto a photo's center no
     matter what combination of programmatic + native scrolling put it
     there. */
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  cursor: ew-resize;
}

.team-track-viewport::-webkit-scrollbar {
  display: none;
}

.team-track {
  display: flex;
  align-items: flex-end;
  height: 100%;
  /* Without an explicit width, a block-level flex container defaults to
     filling its parent (the viewport) rather than sizing to its actual
     content. Its flex children still overflow and scroll correctly, but
     padding-right ends up applied to a box narrower than what's actually
     rendered, so the browser silently drops it from the scrollable area —
     only padding-left (measured from the scroll origin) ever took effect.
     width: max-content sizes the track to its true content + padding on
     both sides, which is what lets the last item actually reach center. */
  width: max-content;
  /* Explicit spacing between photos. .team-item no longer has a fixed
     width (it hugs the photo, which team.js sizes dynamically in compact
     mode), so without this the items would sit flush against each other.
     108px is 50% more than the ~72px gap the old fixed-width items
     produced incidentally (360px item − ~288px photo, centered). */
  gap: 108px;
}

.team-item {
  flex: 0 0 auto;
  /* No fixed width here on purpose: the photo's rendered width follows
     its height (width: auto below) which team.js drives dynamically in
     compact mode, so the item box just hugs whatever that turns out to
     be. Spacious mode and the mobile breakpoint each set a fixed width
     below, at higher specificity, since their photo size is fixed too. */
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  scroll-snap-align: center;
  background: var(--cream);
  opacity: 0.5;
  transition: opacity 0.5s ease;
}

.team-item.is-active {
  opacity: 0.9;
}

.team-photo {
  display: block;
  height: var(--team-photo-h, 300px);
  width: auto;
  mix-blend-mode: darken;
}

.team-divider {
  width: 100%;
  height: 2px;
  background: var(--ink);
  margin-top: 0;
  flex: 0 0 auto;
}

.team-info {
  width: max-content;
  max-width: calc(100% - 38px);
  margin: 0;
  padding: 14px 19px 0;
  box-sizing: border-box;
  text-align: left;
}

.team-name {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 1.05rem;
  margin: 0 0 4px;
  color: var(--ink);
  transition: opacity 0.35s ease;
}

.team-credentials {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 0.85rem;
  margin: 0 0 2px;
  transition: opacity 0.35s ease;
}

.team-role {
  font-family: 'Source Code Pro', monospace;
  font-weight: 400;
  font-size: 0.85rem;
  margin: 0 0 8px;
  transition: opacity 0.35s ease;
}

.team-specialty {
  font-family: 'Source Code Pro', monospace;
  font-weight: 500;
  font-size: 0.85rem;
  margin: 0;
  transition: opacity 0.35s ease;
}

.team-info.is-swapping .team-name,
.team-info.is-swapping .team-credentials,
.team-info.is-swapping .team-role,
.team-info.is-swapping .team-specialty {
  opacity: 0;
}

/* Carousel density toggle — "compact" (the rules above, sized dynamically
   by js/team.js) is the default; ".team-gallery--spacious" restores the
   original looser spacing and larger photo size on demand, via the
   "carousel" button in the view-as switcher above the footer (see
   js/team-versions.js). Desktop only — the mobile media query below
   already uses its own fixed values regardless of density.

   The photo/viewport height is still driven by the --team-photo-h custom
   property js/team.js sets (fitViewportHeight): in spacious mode it's
   capped at 420px (this mode's original fixed size) whenever the window
   is tall enough, and only shrinks below that on a short window — never
   fixed at 420px regardless of viewport, which is what used to force a
   scroll to see the full description on ordinary window heights. Same
   reasoning as the default/compact carousel's .team-photo rule above;
   the 420px here is only the pre-JS/no-JS fallback. .team-item has no
   width override (removed the old fixed 480px) so it keeps hugging the
   photo's actual rendered width at whatever size that turns out to be. */
.team-gallery--spacious {
  /* Was 45px, then 12px — trimmed further (see also the .intro override
     below) to free up more room for the photo above the 260px floor on an
     ordinary laptop screen. */
  margin-top: 0;
}

.team-gallery--spacious .team-gallery-sticky {
  min-height: 480px;
}

.team-gallery--spacious .team-track-viewport {
  height: var(--team-photo-h, 420px);
}

.team-gallery--spacious .team-photo {
  height: var(--team-photo-h, 420px);
}

.team-gallery--spacious .team-info {
  /* Was 26px — trimmed a little for the same reason as the margin-top
     change above: more headroom for the photo before it hits its floor. */
  padding-top: 14px;
}

.team-gallery--spacious .team-role {
  margin-bottom: 16px;
}

@media (max-width: 720px) {
  .team-gallery-sticky {
    min-height: 0;
    /* Base rule above top-aligns (flex-start) so desktop's taller sticky
       box doesn't dump all its slack above the photo before you've even
       scrolled. Mobile has the opposite problem: the box's own height is
       set by js/team.js's fitViewportHeight() to content height + a
       trimmed slice of the leftover viewport space, and top-aligning left
       that whole slice stranded below the info block as a lopsided gap
       before the footer, with the photo pinned flush under the heading.
       Centering splits that same leftover space evenly above and below
       the content instead, so the photo sits lower (breathing room below
       "Meet the team") and the page reads balanced top-to-bottom rather
       than top-heavy. */
    justify-content: center;
  }

  .team-track-viewport,
  /* .team-gallery--spacious .team-track-viewport (below, unconditional)
     is two classes deep, so it otherwise outranks the plain
     .team-track-viewport above regardless of viewport — a media query
     doesn't add specificity, only source order does, and that rule
     comes first. team.html's carousel ships with --spacious on by
     default, so without this the mobile height never actually applied:
     the photo rendered at --team-photo-h's 420px fallback (fine on
     desktop, where fitViewportHeight() sets that variable — but it
     deliberately returns early on mobile without ever setting it, see
     js/team.js) instead of this fixed 320px. Repeating the selector
     here, later in the cascade, restores the fixed mobile height
     regardless of which density class is on the gallery. */
  .team-gallery--spacious .team-track-viewport {
    height: 320px;
    scroll-snap-type: x mandatory;
    cursor: auto;
  }

  .team-item {
    /* No fixed width, same reasoning as the desktop compact carousel:
       the photo (below) is height:320px with width:auto, so its
       rendered width is whatever its aspect ratio makes it — a fixed
       260px box here was narrower than that, so the photo overflowed
       its item and butted up against — or overlapped — its neighbors,
       which is what read as stray "white bars" once a gap was added
       between items. Letting the item hug the photo's actual width
       fixes both. */
    width: auto;
  }

  .team-track {
    /* Deliberately much smaller than the desktop compact carousel's
       108px — this is just a touch of breathing room between photos on
       a narrow screen, not the same spacing goal. */
    gap: 20px;
  }

  .team-photo,
  /* Same cascade fix as .team-track-viewport above: .team-gallery--spacious
     .team-photo is the more specific, unconditional rule that otherwise
     wins at every width, so it needs repeating here to actually apply the
     fixed mobile height. */
  .team-gallery--spacious .team-photo {
    height: 320px;
  }

  .team-divider {
    margin-top: 0;
  }

  .team-info {
    /* Same treatment as desktop: a box that hugs its own longest line
       (name/credentials/role/specialty are rarely the same width) with
       the text left-aligned inside it — the box itself is what's
       centered on the screen (auto margins), not each line of text
       independently. */
    width: max-content !important;
    max-width: calc(100% - 38px);
    margin: 0 auto !important;
    padding: 18px 19px 0;
    text-align: left;
  }
}

/* Team gallery — mobile tap arrows, overlaid directly on the photo.
   Only one team member is visible at a time on a phone-width screen, so
   nothing on screen previously hinted the strip could be swiped, or gave
   a way to move between members without a working swipe gesture. Sitting
   right on the photo's own left/right edges (rather than in a row below,
   which read as disconnected from the thing they control) both cues that
   there's more to see and puts the tap targets exactly where a thumb
   already is. Hidden entirely on desktop, where the wheel-driven carousel
   already makes itself obvious. Reuses team.js's existing goToIndex(), so
   a tap here drives the exact same smooth, centered navigation as a swipe
   or a wheel nudge.
   .team-photo-frame is a plain non-scrolling wrapper around
   .team-track-viewport — the arrows are positioned relative to *it*, not
   the viewport's own scrolling content, so they stay put over the photo
   instead of scrolling away with the strip underneath them. */
.team-photo-frame {
  position: relative;
}

.team-nav-arrow {
  display: none;
}

@media (max-width: 720px) {
  .team-nav-arrow {
    /* appearance: none — otherwise Chrome's native <button> widget can
       impose its own intrinsic sizing on top of (or instead of) the
       width/height set here. */
    appearance: none;
    -webkit-appearance: none;
    display: flex;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid rgba(23, 23, 23, 0.2);
    /* Semi-opaque cream fill (not just an outline) so the arrow stays
       legible sitting on top of a photo, whatever tone that part of the
       image happens to be. */
    background: rgba(241, 238, 227, 0.82);
    color: var(--ink);
    font-family: 'Source Code Pro', monospace;
    font-size: 1.1rem;
    line-height: 1;
    align-items: center;
    justify-content: center;
    padding: 0;
    cursor: pointer;
  }

  .team-nav-prev { left: 10px; }
  .team-nav-next { right: 10px; }

  .team-nav-arrow:disabled {
    opacity: 0.35;
    cursor: default;
  }
}

/* Team page — alternate layouts (grid, directory) + the switcher that
   toggles between all three. Only one [data-team-version] section is
   ever unhidden at a time; see js/team-versions.js. */

.team-version-switcher {
  max-width: 1160px;
  margin: 0 auto;
  padding: 40px 19px 56px;
  display: flex;
  align-items: baseline;
  gap: 16px;
  font-family: 'Source Code Pro', monospace;
}

.team-version-label {
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  opacity: 0.5;
}

.team-version-btn {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 0.9rem;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 0 0 3px;
  margin: 0;
  color: var(--ink);
  opacity: 0.45;
  cursor: pointer;
}

.team-version-btn:hover,
.team-version-btn:focus-visible {
  opacity: 0.8;
}

.team-version-btn.is-active {
  opacity: 1;
  color: var(--coral);
  border-bottom-color: var(--coral);
}

/* -- Grid version -- */

.team-grid-section {
  max-width: 1160px;
  margin: 45px auto 0;
  padding: 0 19px;
}

.team-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 88px 56px;
}

.team-card {
  border-top: 2px solid var(--ink);
  padding-top: 20px;
}

.team-card.accent-coral { border-top-color: var(--coral); }
.team-card.accent-blue { border-top-color: var(--blue); }
.team-card.accent-green { border-top-color: var(--green-deep); }

.team-card-photo {
  width: 100%;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--cream);
  margin-bottom: 16px;
}

.team-card-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
  mix-blend-mode: darken;
  /* Ana's portrait (first card) reads with the punchiest blacks of the six —
     the others sit on brighter source backgrounds that mix-blend darken
     can't compress on its own. Dialing the rest down to a matching opacity
     lets their lights fall into the cream page background the same way
     Ana's does, instead of standing out as a lighter block in the grid. */
  opacity: 0.78;
}

.team-card:first-child .team-card-photo img {
  opacity: 1;
}

.team-card-name {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 1.05rem;
  margin: 0 0 4px;
}

.team-card-credentials {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 0.85rem;
  margin: 0 0 2px;
}

.team-card-role {
  font-family: 'Source Code Pro', monospace;
  font-size: 0.85rem;
  margin: 0 0 10px;
}

.team-card-specialty {
  font-family: 'Source Code Pro', monospace;
  font-weight: 500;
  font-size: 0.85rem;
  margin: 0;
}

@media (max-width: 1000px) {
  .team-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 600px) {
  .team-grid { grid-template-columns: 1fr; }
}

/* -- Directory version -- */

.team-directory-section {
  max-width: 1160px;
  margin: 45px auto 0;
  padding: 0 19px;
}

.team-directory {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: start;
  border-top: 2px solid var(--ink);
}

.team-directory-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.team-directory-item {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 16px 0;
  border-bottom: 1px solid rgba(23, 23, 23, 0.15);
  cursor: pointer;
  font-family: 'Source Code Pro', monospace;
  opacity: 0.5;
  transition: opacity 0.2s ease;
}

.team-directory-item.is-active,
.team-directory-item:hover,
.team-directory-item:focus-visible {
  opacity: 1;
}

.team-directory-num {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--coral);
  flex: 0 0 auto;
}

.team-directory-name {
  font-size: 1rem;
  font-weight: 600;
  flex: 1 1 auto;
}

.team-directory-role {
  font-size: 0.8rem;
  opacity: 0.7;
  flex: 0 0 auto;
  text-align: right;
}

.team-directory-detail {
  padding-top: 16px;
}

.team-directory-photo {
  width: 100%;
  max-width: 320px;
  height: auto;
  display: block;
  background: var(--cream);
  mix-blend-mode: darken;
  margin-bottom: 20px;
}

.team-directory-detail-name {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 1.3rem;
  margin: 0 0 6px;
}

.team-directory-detail-credentials {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 0.85rem;
  margin: 0 0 2px;
}

.team-directory-detail-role {
  font-family: 'Source Code Pro', monospace;
  font-size: 0.85rem;
  margin: 0 0 16px;
}

.team-directory-detail-specialty {
  font-family: 'Source Code Pro', monospace;
  font-weight: 500;
  font-size: 0.85rem;
  margin: 0;
}

@media (max-width: 780px) {
  .team-directory {
    grid-template-columns: 1fr;
  }

  .team-directory-detail {
    order: -1;
    border-bottom: 1px solid rgba(23, 23, 23, 0.15);
    padding-bottom: 24px;
  }
}

/* Publications page */

.pubs-section {
  max-width: 1160px;
  margin: 60px auto 0;
  padding: 0 19px 90px;
}

.pubs-section + .pubs-section {
  margin-top: 0;
  padding-top: 60px;
  border-top: 1px solid rgba(23, 23, 23, 0.12);
}

.pubs-kicker {
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--coral);
  margin: 0 0 10px;
}

.pubs-heading {
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  line-height: 1.2;
  color: var(--ink);
  margin: 0 0 22px;
}

/* Modifier for the "colourful cards, mono headings" variant — swaps just
   the section titles to Source Code Pro. Combined with .pubs-heading
   rather than replacing it, so spacing/margins stay shared across all
   three publications pages. */
.pubs-heading-mono {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: clamp(1.15rem, 2.3vw, 1.5rem);
  letter-spacing: 0.01em;
}

.pubs-count {
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.55;
  margin: 0 0 30px;
}

.pubs-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 56px;
}

.pub-card {
  padding: 26px 0 24px;
  border-top: 2px solid var(--ink);
}

.pub-card.accent-coral { border-top-color: var(--coral); }
.pub-card.accent-blue { border-top-color: var(--blue); }
.pub-card.accent-green { border-top-color: var(--green-deep); }

.pub-media {
  display: block;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  margin: 0 0 14px;
}

.pub-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(100%);
  opacity: 0.8;
  mix-blend-mode: darken;
}

.pub-title {
  display: block;
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  font-size: 1.1rem;
  line-height: 1.32;
  color: var(--ink);
  text-decoration: none;
  margin: 0 0 12px;
}

.pub-title:hover,
.pub-title:focus-visible {
  text-decoration: underline;
  text-decoration-color: var(--coral);
}

.pub-authors {
  font-size: 0.85rem;
  margin: 0 0 4px;
  opacity: 0.85;
}

.pub-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 10px;
  font-size: 0.85rem;
  margin: 0;
}

.pub-journal {
  font-style: italic;
}

.pub-year {
  color: var(--coral);
  font-weight: 600;
}

.pub-card.accent-blue .pub-year { color: var(--blue); }
.pub-card.accent-green .pub-year { color: var(--green-deep); }

@media (max-width: 780px) {
  .pubs-grid {
    grid-template-columns: 1fr;
    gap: 0;
  }
}

.pubs-list {
  display: flex;
  flex-direction: column;
}

.pubs-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px 56px;
}

@media (max-width: 720px) {
  .pubs-columns {
    grid-template-columns: 1fr;
    gap: 44px;
  }
}

.pub-simple .pub-title {
  margin-bottom: 14px;
}

.pub-simple .pub-authors:last-child {
  margin-bottom: 0;
}

/* Publications page — mono variant.
   The nav is the lightest thing on every page: small Source Code Pro links,
   coral, no weight, no boxes, sitting in a lot of quiet cream. The default
   publications page borrows Work Sans headlines and solid 2px accent bars
   for its cards, which reads more like a dashboard than like the nav. This
   variant pulls the card language back toward the nav's own vocabulary —
   monospace titles, coral as the only accent, hairline rules instead of
   bars, more air between entries — so scrolling from the header into the
   list doesn't change register. */

.pubm-card {
  padding: 28px 0;
  border-top: 1px solid rgba(23, 23, 23, 0.15);
}

@media (min-width: 781px) {
  .pubs-grid .pubm-card:nth-child(-n+2) {
    border-top: none;
    padding-top: 0;
  }
}

.pubm-list .pubm-card:first-child {
  border-top: none;
  padding-top: 0;
}

.pubm-title {
  display: block;
  font-family: 'Source Code Pro', monospace;
  font-weight: 500;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--coral);
  text-decoration: none;
  margin: 0 0 10px;
}

p.pubm-title {
  color: var(--ink);
  opacity: 0.85;
}

a.pubm-title:hover,
a.pubm-title:focus-visible {
  text-decoration: underline;
}

.pubm-authors {
  font-size: 0.82rem;
  margin: 0 0 4px;
  opacity: 0.7;
}

.pubm-authors:last-child {
  margin-bottom: 0;
}

.pubm-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 10px;
  font-size: 0.82rem;
  margin: 0;
  opacity: 0.7;
}

.pubm-journal {
  font-style: italic;
}

.pubm-year {
  font-weight: 500;
}

@media (max-width: 780px) {
  .pubs-grid .pubm-card:first-child {
    border-top: none;
    padding-top: 0;
  }
}

/* ==========================================================================
   Site footer — shared structure, four swappable color themes.
   Reuses the tri-color .divider-bar as a signature strip so every theme
   still reads as "lone places" regardless of background color.
   ========================================================================== */

.site-footer {
  position: relative;
  padding: 0 0 40px;
}

.site-footer .divider-bar {
  margin: 0;
}

.footer-inner {
  max-width: 1160px;
  margin: 0 auto;
  padding: 56px 19px 0;
}

.footer-about,
.footer-contact {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.footer-top {
  display: grid;
  grid-template-columns: 1.3fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 48px;
}

/* Alternate to the .footer-partners-tier--lead treatment: instead of a
   labeled row down with the other logos, the host institution's mark
   sits directly above the wordmark in .footer-about — first thing read
   in the footer, no kicker needed since the position alone says "this
   is whose project it is." */

.footer-lead-logo {
  display: block;
  height: 28px;
  width: auto;
  margin: 0 0 16px;
}

/* Third treatment: the host mark sits large on its own at the very top
   of the footer, with the funder and academic-partner rows lined up
   beside it (not below, and not labeled "a project by" — the size and
   position already say that). Reuses .footer-partners-tier/.partner-row
   for the two logo groups so they stay theme-aware, just laid out
   side-by-side instead of stacked. */

.footer-lead-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 44px;
  padding-bottom: 40px;
}

.footer-lead-logo--lg {
  height: 52px;
  width: auto;
  margin: 0;
  flex: 0 0 auto;
}

.footer-lead-groups {
  display: flex;
  flex-wrap: wrap;
  gap: 40px;
}

.footer-lead-groups .footer-partners-tier + .footer-partners-tier {
  margin-top: 0;
}

.footer-wordmark {
  font-family: 'Work Sans', sans-serif;
  font-weight: 400;
  font-size: 1.6rem;
  margin: 0 0 14px;
}

.footer-tagline {
  font-size: 0.85rem;
  max-width: 30em;
  margin: 0;
  opacity: 0.8;
}

.footer-kicker {
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin: 0 0 14px;
  opacity: 0.7;
}

.footer-nav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  font-size: 0.95rem;
}

.footer-nav a {
  text-decoration: none;
}

.footer-nav a:hover,
.footer-nav a:focus-visible {
  text-decoration: underline;
}

.footer-contact .footer-email {
  display: inline-block;
  font-size: 0.95rem;
  text-decoration: none;
  margin-bottom: 10px;
}

.footer-contact .footer-email:hover,
.footer-contact .footer-email:focus-visible {
  text-decoration: underline;
}

.footer-address {
  font-size: 0.82rem;
  margin: 0;
  opacity: 0.75;
  line-height: 1.5;
}

.footer-partners {
  padding: 34px 0 40px;
  border-top: 1px solid rgba(23, 23, 23, 0.12);
}

.partner-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

/* Real-logo variant of .partner-row — used once the placeholder text
   chips are swapped for actual partner artwork. Every mark below is a
   flattened all-white PNG (see assets/icons/partners/*-white.png), so
   they sit directly on the coral fill with no card/box needed. Tiered
   into three rows (funder / academic partners / supporting
   organisations) mirroring the hierarchy in the grant submission doc,
   each with its own kicker. */

.footer-partners-tier {
  /* Without this, the block-level .footer-kicker <p> inside stretches to
     the tier's full width (as wide as its logo row), so the roamer's
     hit-box for a short label like "funded by" reaches far past the
     visible text into blank space beside it. Shrink-wrapping both the
     kicker and the logo row to their own content — same fix as
     footer-about/footer-nav/footer-contact above — keeps the collision
     box honest. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.footer-partners-tier + .footer-partners-tier {
  margin-top: 26px;
}

.footer-partners-tier .partner-row {
  gap: 32px;
  align-items: center;
}

.partner-logo-img {
  display: flex;
  align-items: center;
  position: relative;
}

.partner-logo-img img {
  display: block;
  width: auto;
  height: 34px;
}

/* Recolors the actual mark instead of glowing around it, with no new
   asset needed: footer-roamer.js lazily inserts one of these per touched
   logo, sized to match via `inset: 0` on this `position: relative`
   parent, using the same PNG as a mask so only its own silhouette gets
   painted — with background-color: currentColor, so it picks up whatever
   .roamer-touched sets on the wrapping span (same green-on-coral /
   coral-on-blue swap as everything else). Sits invisible at opacity 0
   until touched; footer-roamer.js drives its opacity transition inline,
   in lockstep with the span's own color/border-color transition, on the
   same snap-then-hold-then-fade timing. */
.partner-logo-glow {
  position: absolute;
  inset: 0;
  background-color: currentColor;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center left;
  mask-position: center left;
  opacity: 0;
  pointer-events: none;
}

/* Larger-mark tiers: the unlabeled top row (ISPUP + academic partners
   ICBAS/FADEUP) and the "in partnership with" row (Sydney/UNSW) use
   bigger, more detailed institutional lockups than the compact "funded
   by" logos, so they get a taller height and a full-opacity kicker
   (instead of the usual 0.7) to read as more prominent rows. */

.footer-partners-tier--lead .footer-kicker {
  opacity: 1;
}

.footer-partners-tier--lead .partner-logo-img img {
  height: 44px;
}

/* ICBAS, Faculdade de Desporto and UNSW use taller, stacked two-line
   lockups (crest + institution name on its own line below), so at the
   same height as the single-line marks beside them (ISPUP, Sydney) their
   type reads noticeably smaller. Bumping just these up keeps every mark
   in the row reading at a comparable visual weight. */
.footer-partners-tier--lead .partner-logo-img--stacked img {
  height: 58px;
}

.partner-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px 20px;
  border: 1px solid rgba(23, 23, 23, 0.25);
  border-radius: 3px;
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.75;
  white-space: nowrap;
}

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px 24px;
  padding: 24px 0 32px;
  border-top: 1px solid rgba(23, 23, 23, 0.12);
  font-size: 0.78rem;
  opacity: 0.65;
}

.footer-bottom p { margin: 0; }

@media (max-width: 780px) {
  .footer-top {
    grid-template-columns: 1fr;
    gap: 32px;
  }
}

/* Theme 1 — cream / minimal. Same background as the rest of the site;
   the only separation is the divider-bar and hairline rules, echoing the
   nav's own lightness rather than boxing the footer off as a distinct zone. */

.site-footer--cream {
  background: var(--cream);
  color: var(--ink);
}

.site-footer--cream .footer-nav a,
.site-footer--cream .footer-contact .footer-email {
  color: var(--coral);
}

.site-footer--cream .footer-kicker {
  color: var(--ink);
}

.site-footer--cream .partner-logo {
  border-color: rgba(23, 23, 23, 0.3);
  color: var(--ink);
}

/* Theme 2 — deep green. Reuses the same saturated green-deep block as the
   .closing section on the project page — the boldest, most "branded"
   option. */

.site-footer--green {
  background: var(--green-deep);
  color: var(--cream);
}

.site-footer--green .footer-nav a,
.site-footer--green .footer-contact .footer-email {
  color: var(--cream);
}

.site-footer--green .footer-kicker {
  color: rgba(241, 238, 227, 0.75);
}

.site-footer--green .footer-partners,
.site-footer--green .footer-bottom {
  border-top-color: rgba(241, 238, 227, 0.25);
}

.site-footer--green .partner-logo {
  border-color: rgba(241, 238, 227, 0.4);
  color: var(--cream);
}

/* Theme 3 — blue. The same blue used for stats and quotes elsewhere on the
   site — a calmer, "research institute" register, with coral kept as the
   one warm accent against it. */

.site-footer--blue {
  background: var(--blue);
  color: var(--cream);
}

.site-footer--blue .footer-nav a,
.site-footer--blue .footer-contact .footer-email {
  color: var(--cream);
}

.site-footer--blue .footer-nav a:hover,
.site-footer--blue .footer-contact .footer-email:hover {
  color: var(--coral);
}

.site-footer--blue .footer-kicker {
  color: var(--coral);
  opacity: 1;
}

.site-footer--blue .footer-partners,
.site-footer--blue .footer-bottom {
  border-top-color: rgba(241, 238, 227, 0.25);
}

.site-footer--blue .partner-logo {
  border-color: rgba(241, 238, 227, 0.4);
  color: var(--cream);
}

/* Theme 4 — coral. The warmest option; ink used as the grounding accent
   since coral-on-coral would disappear. */

.site-footer--coral {
  background: var(--coral);
  color: var(--cream);
}

.site-footer--coral .footer-nav a,
.site-footer--coral .footer-contact .footer-email {
  color: var(--cream);
}

.site-footer--coral .footer-nav a:hover,
.site-footer--coral .footer-contact .footer-email:hover {
  color: var(--ink);
}

.site-footer--coral .footer-kicker {
  color: rgba(23, 23, 23, 0.7);
  opacity: 1;
}

.site-footer--coral .footer-partners,
.site-footer--coral .footer-bottom {
  border-top-color: rgba(241, 238, 227, 0.35);
}

.site-footer--coral .partner-logo {
  border-color: rgba(241, 238, 227, 0.5);
  color: var(--cream);
}

/* Demo-only chrome for the footer concepts showcase page — not part of the
   reusable footer component itself. */

.concept-label {
  max-width: 1160px;
  margin: 0 auto;
  padding: 30px 19px 0;
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.55;
}

.concept-label span {
  color: var(--coral);
  opacity: 1;
}

/* ==========================================================================
   Footer layout variations — same tokens (wordmark, kicker, partner-logo,
   footer-nav) rearranged into four different structures. Demoed only in
   coral and green-deep, per request, rather than repeating all four colors.
   ========================================================================== */

/* Layout A — centered / stacked. Everything on one axis, echoing the
   .closing section's centered, editorial calm. */

.footer-inner--centered {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-top: 60px;
}

.footer-inner--centered .footer-wordmark { margin-bottom: 10px; }
.footer-inner--centered .footer-tagline { margin-bottom: 30px; }

.footer-nav--inline {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 22px;
  margin-bottom: 40px;
}

.footer-partners--centered {
  border-top: none;
  padding-top: 0;
  width: 100%;
}

.footer-partners--centered .footer-kicker { text-align: center; }

.partner-row--centered {
  justify-content: center;
}

.footer-bottom--centered {
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
  border-top: none;
}

/* Layout B — split panel. Two color blocks meeting in the middle, the one
   design here that puts coral and green to work side by side rather than
   as alternates. */

.footer-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  padding-bottom: 0;
}

.footer-split .divider-bar {
  grid-column: 1 / -1;
}

.split-panel {
  padding: 56px 32px 48px;
  color: var(--cream);
}

.split-panel--green { background: var(--green-deep); }
.split-panel--coral { background: var(--coral); }

.split-panel-inner {
  max-width: 460px;
}

.split-panel--green .split-panel-inner { margin-left: auto; }
.split-panel--coral .split-panel-inner { margin-right: auto; }

.split-panel a { color: var(--cream); text-decoration: none; }
.split-panel a:hover,
.split-panel a:focus-visible { text-decoration: underline; }

.split-panel .footer-kicker { color: rgba(241, 238, 227, 0.75); }

.split-panel .partner-logo {
  border-color: rgba(241, 238, 227, 0.45);
  color: var(--cream);
}

.split-panel .footer-copy {
  margin-top: 28px;
  font-size: 0.78rem;
  opacity: 0.75;
}

@media (max-width: 780px) {
  .footer-split { grid-template-columns: 1fr; }
  .split-panel--green .split-panel-inner,
  .split-panel--coral .split-panel-inner {
    margin-left: 0;
    margin-right: 0;
    max-width: none;
  }
}

/* Layout C — marquee / logo wall. Partners are the headline; everything
   else compresses into one slim strip underneath. */

.logo-wall {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin: 22px 0 48px;
}

.logo-wall .partner-logo {
  padding: 26px 10px;
  font-size: 0.8rem;
}

@media (max-width: 600px) {
  .logo-wall { grid-template-columns: repeat(2, 1fr); }
}

.footer-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 28px;
  padding: 24px 0 32px;
  border-top: 1px solid rgba(241, 238, 227, 0.35);
  font-size: 0.8rem;
}

.footer-strip .footer-wordmark {
  font-size: 1.1rem;
  margin: 0;
}

.footer-strip .footer-nav--inline {
  margin-bottom: 0;
  gap: 6px 18px;
  font-size: 0.85rem;
}

/* Layout D — compact utility bar. The quietest possible closer: two thin
   rows, nothing given more visual weight than anything else. */

.footer-inner--compact { padding-top: 32px; }

.compact-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 28px;
  padding: 18px 0;
}

.compact-row--sub {
  border-top: 1px solid rgba(241, 238, 227, 0.25);
  font-size: 0.78rem;
  opacity: 0.75;
}

.compact-row .footer-wordmark {
  font-size: 1.1rem;
  margin: 0;
}

.compact-row .footer-nav--inline {
  margin-bottom: 0;
  gap: 6px 16px;
  font-size: 0.85rem;
}

.partner-row--tiny {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.partner-logo--tiny {
  padding: 6px 10px;
  font-size: 0.65rem;
}

/* ==========================================================================
   Footer round 2 — back to the grid-column structure (about+nav+contact,
   partner row, bottom bar), three new color/accent treatments instead of
   new layouts.
   ========================================================================== */

/* Theme 5 — dark ink. Matches the .dark-section / .invert-section already
   used on team.html, press.html and project.html, so this reads as an
   existing pattern rather than a new idea. */

.site-footer--ink {
  background: #282828;
  color: var(--cream);
}

.site-footer--ink .footer-nav a,
.site-footer--ink .footer-contact .footer-email {
  color: var(--green-light);
}

.site-footer--ink .footer-kicker {
  color: var(--green-light);
  opacity: 1;
}

.site-footer--ink .footer-partners,
.site-footer--ink .footer-bottom {
  border-top-color: rgba(241, 238, 227, 0.18);
}

.site-footer--ink .partner-logo {
  border-color: rgba(241, 238, 227, 0.35);
  color: var(--cream);
}

/* Theme 6 — cream, with the partner logos filled in rotating solid color
   instead of outlined. Keeps the page's usual light background but brings
   the colourfulness back by way of the partners themselves. */

.partner-logo--fill-coral { background: var(--coral); border-color: var(--coral); color: var(--cream); }
.partner-logo--fill-blue { background: var(--blue); border-color: var(--blue); color: var(--cream); }
.partner-logo--fill-green { background: var(--green-deep); border-color: var(--green-deep); color: var(--cream); }

/* Theme 7 — mint (green-light). Softer and higher-key than the deep-green
   option from round 1; dark ink text since green-light is too pale for
   cream-on-green to read. */

.site-footer--mint {
  background: var(--green-light);
  color: var(--ink);
}

.site-footer--mint .footer-nav a,
.site-footer--mint .footer-contact .footer-email {
  color: var(--coral);
}

.site-footer--mint .footer-kicker {
  color: var(--ink);
  opacity: 0.75;
}

.site-footer--mint .footer-partners,
.site-footer--mint .footer-bottom {
  border-top-color: rgba(23, 23, 23, 0.18);
}

.site-footer--mint .partner-logo {
  border-color: rgba(23, 23, 23, 0.35);
  color: var(--ink);
}

/* Preview-only switcher control — lets one footer show at a time on every
   page while comparing options. A small floating pair of arrows pinned to
   the bottom-right corner, out of the way of the actual content. Not part
   of the shipped design; remove once a favorite is picked. */

.footer-switcher {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 20;
  display: flex;
  gap: 6px;
}

.footer-switcher-btn {
  font-family: 'Source Code Pro', monospace;
  font-weight: 600;
  font-size: 0.7rem;
  background: var(--cream);
  border: 1px solid var(--ink);
  border-radius: 50%;
  width: 22px;
  height: 22px;
  line-height: 1;
  color: var(--ink);
  opacity: 0.55;
  cursor: pointer;
}

.footer-switcher-btn:hover,
.footer-switcher-btn:focus-visible {
  opacity: 1;
  background: var(--coral);
  border-color: var(--coral);
  color: var(--cream);
}

.footer-switcher-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Green details on the coral/blue footers — small, quiet, playful rather
   than a third brand color competing for attention. */

.partner-logo {
  transition: border-color 0.2s ease, color 0.2s ease;
}

.partner-logo:hover {
  border-color: var(--green-light);
  color: var(--green-light);
}

/* Roaming happy-face — drifts around the whole footer (including the
   divider-bar line at the top), wraps left/right, bounces top/bottom, and
   nudges off the wordmark, tagline, nav links, kicker labels, email,
   address, partner-logo text chips and partner-logo-img marks it passes
   over. Driven entirely by js/footer-roamer.js; this just sizes and
   colors it. footer-about/footer-nav/footer-contact/footer-partners-tier
   were all changed above (align-items: flex-start) so their hit boxes hug
   the actual text/logos instead of stretching to the full grid-column
   width — otherwise the gap between columns was narrower than the roamer
   itself, and short kicker labels left blank space that still read as
   "solid" to the collision check. */

.footer-roamer {
  position: absolute;
  top: 0;
  left: 0;
  width: 74px;
  height: 74px;
  color: var(--green-light);
  /* Touch mode: every touch just toggles .sad, and this transition is
     what animates that flip. Spin mode ignores this rule entirely — it
     drives color continuously from the rotation angle every frame
     instead (see updateSpinMood()), via inline style, which always wins
     over this. */
  transition: color 0.6s ease;
  pointer-events: none;
  will-change: transform;
}

.footer-roamer svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* Reused straight from the loneliness hero on index.html: the mouth's `d`
   morphs between a smile and a frown, and here the whole face's color
   flips with it — repurposed as a "touched something" reaction instead of
   a scroll-driven mood. */

.footer-roamer.sad .mood-mouth {
  d: path("M32,64 Q50,48 68,64");
}

/* Coral footer: touched elements turn green, the face turns blue + sad.
   Touching the same element again reverts both. */

.site-footer--coral .footer-roamer.sad {
  color: var(--blue);
}

.site-footer--coral .roamer-touched {
  color: var(--green-light) !important;
}

.site-footer--coral .partner-logo.roamer-touched {
  border-color: var(--green-light) !important;
}

/* Blue footer: both the touched element and the face turn coral/red. */

.site-footer--blue .footer-roamer.sad {
  color: var(--coral);
}

.site-footer--blue .roamer-touched {
  color: var(--coral) !important;
}

.site-footer--blue .partner-logo.roamer-touched {
  border-color: var(--coral) !important;
}

/* No transition declared here on purpose — footer-roamer.js drives the
   timing directly via inline style, so it can snap instantly to the
   touched color and then fade back out over exactly 1 second. */

/* ==========================================================================
   Version navigator — sits just above the footer on every publications
   page, right-aligned by default, so you can jump to any of the other
   variants from wherever you are. The current page shows as plain
   (non-link) text. Also reused (with the --left modifier) as the plain
   two-way toggle between the "scientific papers" and "outputs register"
   pages — same quiet text-link treatment, just anchored to the left side
   above the footer instead of a standalone segmented control.
   ========================================================================== */

.version-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: baseline;
  gap: 8px 16px;
  max-width: 1160px;
  margin: 0 auto;
  padding: 50px 19px 0;
  font-family: 'Source Code Pro', monospace;
  font-size: 0.8rem;
}

.version-nav--left {
  justify-content: flex-start;
}

.version-nav-label {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--ink);
  opacity: 0.5;
  margin-right: 4px;
}

.version-nav a,
.version-nav span.is-current {
  text-decoration: none;
}

.version-nav a {
  color: var(--coral);
}

.version-nav a:hover,
.version-nav a:focus-visible {
  text-decoration: underline;
}

.version-nav span.is-current {
  color: var(--ink);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-color: var(--coral);
  text-underline-offset: 3px;
}

@media (max-width: 600px) {
  .version-nav {
    justify-content: flex-start;
  }
}

/* ==========================================================================
   Scrapbook — a looser, more casual take on the Scientific Papers page.
   Cards read like index cards pinned to a board: each .pub-card becomes a
   bordered, shadowed rectangle that sits at a small alternating tilt,
   settling flat on hover/focus so it stays readable rather than just
   decorative. Kickers get a "washi tape" treatment (colored block behind
   the text, rotated), years become small rounded stamp badges, and the
   Scientific Papers grid reflows as a two-column masonry (CSS multicol)
   instead of a strict row grid, so card heights vary naturally.
   ========================================================================== */

.page-scrapbook .pubs-grid {
  display: block;
  column-count: 2;
  column-gap: 44px;
}

/* Registering --float-y as a proper <length> lets the browser interpolate
   it frame-by-frame like any animatable numeric property, instead of
   snapping between the keyframe stops the way a plain custom property
   would. That's what actually fixes the "stepping" — animating `top`
   directly is a layout-triggering property that a masonry (column-count)
   container reflows on every frame, which reads as choppy even with an
   ease-in-out timing function. Composited transforms don't have that
   problem. */
@property --float-y {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

/* Hover no longer writes a literal `transform` — it can't, without
   snapping. A CSS transition can't smoothly aim at a value that's itself
   changing every frame (the still-running float animation), so on
   un-hover the old version jumped straight to wherever the resumed
   animation happened to be. Instead, hover just nudges two of its own
   registered, independently-transitionable custom properties: --hover-
   lift (an extra translateY) and --hover-flat (how much to cancel the
   tilt, 0–1). `transform` reads all three inputs — --tilt, --float-y,
   and the hover pair — every frame, so the hover-in/out transition and
   the paused/resumed float blend into the same continuous value instead
   of two separate systems fighting over one property. */
@property --hover-lift {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}

@property --hover-flat {
  syntax: '<number>';
  inherits: false;
  initial-value: 0;
}

.page-scrapbook .pub-card {
  display: block;
  break-inside: avoid;
  margin: 0 0 34px;
  padding: 22px 22px 24px;
  background: var(--cream);
  border: 1px solid rgba(23, 23, 23, 0.14);
  border-radius: 2px;
  box-shadow: 0 2px 0 rgba(23, 23, 23, 0.05), 0 14px 24px -14px rgba(23, 23, 23, 0.18);
  position: relative;
  --tilt: 0deg;
  --float-y: 0px;
  --hover-lift: 0px;
  --hover-flat: 0;
  transform: rotate(calc(var(--tilt) * (1 - var(--hover-flat)))) translateY(calc(var(--float-y) + var(--hover-lift)));
  transition: --hover-lift 0.25s ease, --hover-flat 0.25s ease, box-shadow 0.25s ease;
  animation-name: scrapbook-float;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

/* Only --float-y is animated — never `transform` itself — so pausing/
   resuming it on hover never jumps: a paused animation just holds its
   last value and continues from exactly there. */
@keyframes scrapbook-float {
  0%, 100% { --float-y: 0px; }
  50% { --float-y: -6px; }
}

.page-scrapbook .pubs-grid .pub-card:nth-child(4n+1) { --tilt: -1.3deg; animation-duration: 6.2s; animation-delay: 0s; }
.page-scrapbook .pubs-grid .pub-card:nth-child(4n+2) { --tilt: 1deg; animation-duration: 5.6s; animation-delay: -1.4s; }
.page-scrapbook .pubs-grid .pub-card:nth-child(4n+3) { --tilt: -0.6deg; animation-duration: 6.8s; animation-delay: -2.9s; }
.page-scrapbook .pubs-grid .pub-card:nth-child(4n+4) { --tilt: 1.5deg; animation-duration: 5.9s; animation-delay: -4.1s; }

.page-scrapbook .pubs-list .pub-card:nth-child(3n+1) { --tilt: -1deg; animation-duration: 6.4s; animation-delay: -0.8s; }
.page-scrapbook .pubs-list .pub-card:nth-child(3n+2) { --tilt: 0.8deg; animation-duration: 5.8s; animation-delay: -2.3s; }
.page-scrapbook .pubs-list .pub-card:nth-child(3n+3) { --tilt: -0.5deg; animation-duration: 6.6s; animation-delay: -3.6s; }

.page-scrapbook .pubs-list .pub-card:nth-child(even) {
  margin-left: 48px;
}

.page-scrapbook .pub-card:hover,
.page-scrapbook .pub-card:focus-within {
  --hover-lift: -2px;
  --hover-flat: 1;
  box-shadow: 0 4px 0 rgba(23, 23, 23, 0.07), 0 20px 30px -14px rgba(23, 23, 23, 0.21);
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  .page-scrapbook .pub-card {
    animation: none;
  }
}

.page-scrapbook .pub-year {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.3em;
  padding: 3px 9px;
  border-radius: 50%;
  background: var(--coral);
  color: var(--cream);
  font-weight: 600;
  transform: rotate(-6deg);
}

.page-scrapbook .pub-card.accent-blue .pub-year { background: var(--blue); }
.page-scrapbook .pub-card.accent-green .pub-year { background: var(--green-deep); }

.page-scrapbook .pubs-kicker {
  display: inline-block;
  padding: 3px 10px;
  background: rgba(226, 74, 43, 0.14);
  transform: rotate(-2deg);
  border-radius: 1px;
}

.page-scrapbook .pubs-section:nth-of-type(3) .pubs-kicker {
  background: rgba(29, 47, 166, 0.12);
  transform: rotate(1.5deg);
}

.page-scrapbook .pubs-section:nth-of-type(4) .pubs-kicker {
  background: rgba(0, 209, 104, 0.14);
  transform: rotate(-1.5deg);
}

.page-scrapbook .pubs-section:nth-of-type(4) .pubs-columns {
  grid-template-columns: 1fr 1.5fr;
  align-items: start;
}

.page-scrapbook .pubs-section:nth-of-type(4) .pubs-columns > div:last-child {
  margin-top: 34px;
}

@media (max-width: 820px) {
  .page-scrapbook .pubs-grid {
    column-count: 1;
  }

  .page-scrapbook .pubs-list .pub-card:nth-child(even) {
    margin-left: 0;
  }
}

@media (max-width: 720px) {
  .page-scrapbook .pubs-section:nth-of-type(4) .pubs-columns {
    grid-template-columns: 1fr;
  }

  .page-scrapbook .pubs-section:nth-of-type(4) .pubs-columns > div:last-child {
    margin-top: 0;
  }
}

/* ==========================================================================
   Scrapbook (lined) — the exact scrapbook page above (same body also
   carries .page-scrapbook, so it gets the tilt/float/shadow/badge/tape
   treatment for free), plus the colored top-border line from the
   original grid cards reinstated as a signature accent. Only the
   border-top sub-properties are touched, so the thin neutral border on
   the other three sides — and everything else about the card — stays
   exactly as scrapbook defines it.
   ========================================================================== */

.page-scrapbook-lines .pub-card {
  border-top-width: 3px;
  border-top-style: solid;
}

.page-scrapbook-lines .pub-card.accent-coral { border-top-color: var(--coral); }
.page-scrapbook-lines .pub-card.accent-blue { border-top-color: var(--blue); }
.page-scrapbook-lines .pub-card.accent-green { border-top-color: var(--green-deep); }

/* ==========================================================================
   Cascade — a second, editorial take on the loose/asymmetric idea. No
   tilt this time (that's scrapbook's signature) — instead a staggered
   vertical rhythm where alternating cards in the grid ride higher or
   lower than their neighbor, a small folded-corner accent per card
   (color-coded to match its usual border accent), italic titles for a
   more expressive editorial feel, and a diagonal three-color rule
   between sections instead of a straight one.
   ========================================================================== */

.page-cascade .pubs-section + .pubs-section {
  border-top: none;
  position: relative;
}

.page-cascade .pubs-section + .pubs-section::before {
  content: '';
  position: absolute;
  top: 0;
  left: 19px;
  right: 19px;
  height: 2px;
  background: linear-gradient(100deg, var(--coral) 0 33%, var(--blue) 33% 66%, var(--green-deep) 66% 100%);
  transform: rotate(-0.5deg);
  transform-origin: left center;
}

.page-cascade .pubs-grid {
  align-items: start;
}

.page-cascade .pubs-grid .pub-card {
  position: relative;
  padding: 26px 28px 24px 0;
}

.page-cascade .pubs-grid .pub-card::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0 20px 20px 0;
  border-color: transparent transparent transparent transparent;
}

.page-cascade .pubs-grid .pub-card.accent-coral::after { border-color: transparent var(--coral) transparent transparent; }
.page-cascade .pubs-grid .pub-card.accent-blue::after { border-color: transparent var(--blue) transparent transparent; }
.page-cascade .pubs-grid .pub-card.accent-green::after { border-color: transparent var(--green-deep) transparent transparent; }

.page-cascade .pubs-grid .pub-card:nth-child(2n) {
  margin-top: 44px;
}

.page-cascade .pubs-grid .pub-card:nth-child(6n) {
  margin-top: 14px;
}

.page-cascade .pub-title {
  font-size: 1.2rem;
  font-style: italic;
}

.page-cascade .pubs-section:nth-of-type(3) .pubs-heading,
.page-cascade .pubs-section:nth-of-type(4) .pubs-heading {
  margin-left: 34px;
}

.page-cascade .pubs-section:nth-of-type(4) .pubs-columns {
  grid-template-columns: 1.4fr 1fr;
  align-items: start;
}

.page-cascade .pubs-section:nth-of-type(4) .pubs-columns > div:last-child {
  margin-top: 40px;
}

@media (max-width: 780px) {
  .page-cascade .pubs-grid .pub-card:nth-child(2n),
  .page-cascade .pubs-grid .pub-card:nth-child(6n) {
    margin-top: 0;
  }
}

@media (max-width: 720px) {
  .page-cascade .pubs-section:nth-of-type(3) .pubs-heading,
  .page-cascade .pubs-section:nth-of-type(4) .pubs-heading {
    margin-left: 0;
  }

  .page-cascade .pubs-section:nth-of-type(4) .pubs-columns {
    grid-template-columns: 1fr;
  }

  .page-cascade .pubs-section:nth-of-type(4) .pubs-columns > div:last-child {
    margin-top: 0;
  }
}
