:root {
  /* Core brand colors — see styling.md */
  --clarkson-green: #004e42;
  --clarkson-green-dark: #00332b;
  --clarkson-gold: #ffcd00;

  /* Secondary — bold */
  --bold-olive: #7a9a01;
  --bold-red: #cf4520;
  --bold-blue: #418fde;
  --bold-navy: #002d72;

  /* Secondary — mature */
  --mature-olive: #a2a569;
  --mature-amber: #c6893f;
  --mature-blue: #7bafd4;
  --mature-navy: #005587;

  /* Neutrals */
  --warm-gray-1: #d7d2cb;
  --warm-gray-6: #a59c94;
  --warm-gray-11: #6e6259;

  /* Site tokens — "Hazard & Blueprint" direction. Ground is a near-black chassis
     ink (not a flat neutral); Clarkson green carries structural panels; gold is
     promoted to a real hazard-stripe accent, not a hover tint. See styling.md. */
  --ink: #0a0f0c;
  --panel: #101c17;
  --panel-raised: #142620;
  --bg: var(--ink);
  --bg-deep: var(--clarkson-green);
  --hazard-black: #050505;
  --hazard-gold: var(--clarkson-gold);
  --line-soft: rgba(226, 232, 227, 0.14);
  --line-strong: rgba(226, 232, 227, 0.32);
  --text: #f3f0e6;
  --text-muted: #b3c7b9;
  --card-bg: var(--panel);
  --border: var(--line-soft);

  /* Type */
  --font-display: "Oswald", "Arial Narrow", sans-serif;
  --font-body: "Barlow", "Segoe UI", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", "Consolas", monospace;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

/* Every target the nav jumps to. The header is fixed, so without this a jump
   stops with the section's heading tucked up behind it — and the header is
   roughly twice its usual height on a phone, where the five links wrap to two
   rows. 5rem clears it there once the section's own top padding is added, and
   reads as deliberate breathing room on a desktop rather than as a gap. */
#our-car,
#subteams,
#leadership,
#sponsors,
#contact,
#join {
  scroll-margin-top: 5rem;
}

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
}

a {
  color: inherit;
  text-decoration: none;
}

::selection {
  background: var(--clarkson-gold);
  color: var(--hazard-black);
}

:focus-visible {
  outline: 2px solid var(--clarkson-gold);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- Header ---------- */

.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.85rem 1.25rem;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.6), transparent);
  border-bottom: 1px solid transparent;
}

@media (max-width: 480px) {
  .logo {
    font-size: 0.95rem;
  }
  .site-header nav {
    gap: 0.35rem 1rem;
  }
  .site-header nav a {
    font-size: 0.8rem;
  }
}

.site-header.solid {
  background: var(--bg-deep);
  border-bottom: 1px solid var(--line-soft);
}

.logo {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.logo-icon {
  height: 2rem;
  width: auto;
  display: block;
}

/* Flex with a gap rather than a margin on each link: with four items the nav
   wraps on a phone, and a left margin would indent the start of the wrapped
   row and push its last link into the viewport edge. */
.site-header nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.4rem 1.5rem;
}

.site-header nav a {
  font-family: var(--font-mono);
  color: var(--text);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.85;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: opacity 0.2s ease, border-color 0.2s ease;
}

.site-header nav a:hover {
  opacity: 1;
  border-color: var(--clarkson-gold);
  color: var(--clarkson-gold);
}

/* The telemetry viewer link: a members' tool rather than a section of the
   site, so it trails the five section links behind a hairline in the muted
   text colour. That colour carries the step down on its own: at the other
   links' 0.85 opacity it would fall under 4.5:1 on the green bar. The rule is
   a pseudo-element, centred in the nav gap, so the hover underline's
   border-color never reaches it. */
.site-header nav .nav-tool {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--text-muted);
  opacity: 1;
}

.site-header nav .nav-tool::before {
  content: "";
  position: absolute;
  top: 15%;
  bottom: 15%;
  left: -0.75rem;
  border-left: 1px solid var(--line-strong);
}

/* A trace line, stroked like the site's other drawn icons. */
.nav-tool-icon {
  flex: none;
  width: 16px;
  height: 12px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Between the phone menu and 1000px the wordmark, the five section links and
   a labelled sixth no longer fit on one row, and the nav would drop to a
   second one. The label steps out of sight there (still the link's name to a
   screen reader, and the title gives it a tooltip), leaving the glyph, and
   the gaps close up by the glyph's width so the row breaks no sooner than it
   did with five links. */
@media (min-width: 769px) and (max-width: 999px) {
  .site-header nav {
    column-gap: 1rem;
  }

  .site-header nav .nav-tool::before {
    left: -0.5rem;
  }

  .nav-tool-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* ---------- Phone menu toggle ----------

   Off above 768px, where the wordmark and the five links still fit on one
   line. Below it they do not: the nav wrapped to a second row and the wordmark
   to two lines, which stacked into a 130px header — a fifth of a phone screen
   spent on navigation before any of the page showed. */

.nav-menu {
  display: none;
}

.nav-menu-button {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  /* A 44px target (the usual floor for a thumb) around a 22px glyph, with the
     overhang pulled back so the bars still line up on the header's padding
     edge rather than floating inside it. */
  width: 44px;
  height: 44px;
  margin: -0.4rem -0.4rem -0.4rem 0;
  padding-right: 0.4rem;
  cursor: pointer;
  list-style: none;
  color: var(--text);
  transition: color 0.2s ease;
}

.nav-menu-button::-webkit-details-marker {
  display: none;
}

.nav-menu-button:hover {
  color: var(--clarkson-gold);
}

/* Three ruled lines: the middle one is the element, the outer two its
   pseudo-elements, so the glyph costs one span. */
.nav-menu-bars {
  position: relative;
}

.nav-menu-bars,
.nav-menu-bars::before,
.nav-menu-bars::after {
  display: block;
  width: 22px;
  height: 1.6px;
  background: currentColor;
  transition: transform 0.2s ease, background 0.2s ease;
}

.nav-menu-bars::before,
.nav-menu-bars::after {
  content: "";
  position: absolute;
  left: 0;
}

.nav-menu-bars::before { top: -7px; }
.nav-menu-bars::after { top: 7px; }

/* Open: the outer two rules fold into a cross and the middle one drops out. */
.nav-menu[open] .nav-menu-bars {
  background: transparent;
}

.nav-menu[open] .nav-menu-bars::before {
  transform: translateY(7px) rotate(45deg);
}

.nav-menu[open] .nav-menu-bars::after {
  transform: translateY(-7px) rotate(-45deg);
}

@media (max-width: 768px) {
  .nav-menu {
    display: block;
  }

  /* Basis 0 rather than the wordmark's own width: at max-content the wordmark
     claims the whole line and pushes the toggle onto a second one, left-aligned
     under it. Given the remaining space instead, it wraps to two lines beside
     the toggle and the header is one band again. */
  .logo {
    flex: 1 1 0;
    min-width: 0;
  }

  /* The links are the toggle's content in every sense but the DOM's — they sit
     beside the <details> rather than inside it so that they can still be a
     plain row above this breakpoint. */
  .site-header nav {
    display: none;
  }

  .nav-menu[open] ~ nav {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    gap: 0;
    margin-top: 0.85rem;
    border-top: 1px solid var(--line-soft);
    animation: nav-unfold 0.25s cubic-bezier(0.16, 1, 0.3, 1);
  }

  /* Full-width rows, so the hover rule reads as a menu row and the tap target
     is the whole width rather than the word. Kept right-aligned: the nav is
     right-aligned everywhere else, and the toggle that opened it is up there. */
  .nav-menu[open] ~ nav a {
    padding: 0.85rem 0;
    text-align: right;
    opacity: 1;
  }

  /* In the menu the telemetry link's rule turns to run across the top of its
     row, splitting it from the section links above. It is a flex row for its
     glyph, so it is pushed right by justify-content, not text-align. */
  .nav-menu[open] ~ nav .nav-tool {
    justify-content: flex-end;
  }

  .nav-menu[open] ~ nav .nav-tool::before {
    top: 0;
    bottom: auto;
    left: 0;
    right: 0;
    border-left: 0;
    border-top: 1px solid var(--line-soft);
  }

  /* Over the hero the header is a transparent gradient, which would leave an
     open menu sitting on the team photo. Solid for as long as it is open —
     the same treatment the header takes on once the page scrolls. */
  .site-header:has(.nav-menu[open]) {
    background: var(--bg-deep);
    border-bottom: 1px solid var(--line-soft);
  }
}

/* <details> can't transition its own height; the panel settles in instead, the
   same move the Join panel makes. */
@keyframes nav-unfold {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---------- Hero (fixed background that fades on scroll) ---------- */

.hero {
  position: fixed;
  inset: 0;
  height: 100vh;
  width: 100%;
  background-image: url("images/TeamPicture.jpeg");
  background-size: cover;
  background-position: center;
  z-index: -2;
}

.hero-overlay {
  position: fixed;
  inset: 0;
  height: 100vh;
  width: 100%;
  z-index: -1;
  background: var(--bg);
  opacity: 0;
}

.hero-content {
  position: relative;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 1.5rem;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0.4) 62%, rgba(10, 15, 12, 0.92) 100%);
  overflow: hidden;
}

.hero-content::before {
  content: "";
  position: absolute;
  inset: 28px;
  border: 1px solid var(--line-strong);
  pointer-events: none;
  animation: frame-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: 0.05s;
}

@keyframes frame-in {
  from { opacity: 0; inset: 46px; }
  to { opacity: 1; inset: 28px; }
}

.crosshair {
  position: absolute;
  width: 28px;
  height: 28px;
  color: var(--line-strong);
  animation: mark-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.crosshair svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}

.crosshair--tl { top: 28px; left: 28px; transform: translate(-50%, -50%); animation-delay: 0.15s; }
.crosshair--tr { top: 28px; right: 28px; transform: translate(50%, -50%); animation-delay: 0.25s; }
.crosshair--bl { bottom: 28px; left: 28px; transform: translate(-50%, 50%); animation-delay: 0.35s; }
.crosshair--br { bottom: 28px; right: 28px; transform: translate(50%, 50%); animation-delay: 0.45s; }

@keyframes mark-in {
  from { opacity: 0; transform: translate(var(--tx, -50%), var(--ty, -50%)) scale(0.5); }
  to { opacity: 1; transform: translate(var(--tx, -50%), var(--ty, -50%)) scale(1); }
}

.crosshair--tl { --tx: -50%; --ty: -50%; }
.crosshair--tr { --tx: 50%; --ty: -50%; }
.crosshair--bl { --tx: -50%; --ty: 50%; }
.crosshair--br { --tx: 50%; --ty: 50%; }

.hero-copy {
  position: relative;
  animation: copy-in 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: 0.55s;
}

@keyframes copy-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.hero-content h1 {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  font-size: clamp(2rem, 5vw, 3.75rem);
  margin: 0 0 0.6rem;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.6);
}

.hero-content .accent {
  color: var(--clarkson-gold);
}

.hero-content p {
  font-family: var(--font-body);
  font-size: clamp(1rem, 2vw, 1.2rem);
  color: var(--text);
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
  max-width: 38rem;
  margin: 0 auto;
}

.scroll-cue {
  position: absolute;
  bottom: 2rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  animation: bob 2s infinite ease-in-out;
}

.scroll-cue svg {
  width: 14px;
  height: 9px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@keyframes bob {
  0%, 100% { transform: translate(-50%, 0); }
  50% { transform: translate(-50%, 8px); }
}

/* ---------- Main content sits below the fixed hero ---------- */

.page-content {
  position: relative;
  z-index: 1;
  margin-top: 100vh;
  background: var(--bg);
}

/* ---------- Our Car (home, above the subteams) ---------- */

.car-section {
  padding: 4.5rem 2rem 3.5rem;
  max-width: 1200px;
  margin: 0 auto;
  border-bottom: 1px solid var(--line-soft);
}

.car-heading {
  text-align: center;
  margin-bottom: 2.5rem;
}

.car-heading h2 {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: 2rem;
  margin: 0;
}

/* The glamour shot is a 3:4 portrait, so it is set beside the copy rather than
   run full-bleed: a full-width band of it would either crop the car out or
   swallow the viewport. The photo column is the narrower of the two so the
   plate stays a readable height, and the copy is centered against it. */
.car-layout {
  display: grid;
  grid-template-columns: minmax(0, 42%) minmax(0, 1fr);
  gap: 2.5rem;
  align-items: center;
  /* Narrower than the section itself (which keeps the full 1200px so its rule
     lines up with the grid below) so the copy track lands near the site's
     ~60ch measure instead of running the full column width. */
  max-width: 940px;
  margin-inline: auto;
}

.car-photo {
  margin: 0;
}

.car-copy p {
  line-height: 1.6;
  color: var(--text);
  max-width: 62ch;
  margin: 0 0 1.1rem;
}

.car-copy p:last-child {
  margin-bottom: 0;
}

@media (max-width: 860px) {
  .car-layout {
    grid-template-columns: 1fr;
    gap: 1.75rem;
  }

  /* justify-self rather than auto margins: the plate is a grid item, and a
     stretched item that is then clamped by max-width keeps its auto margins
     resolved at 0, which would leave the photo stuck to the left edge. */
  .car-photo {
    max-width: 22rem;
    justify-self: center;
  }
}

.subteams-section {
  padding: 4rem 2rem;
  max-width: 1200px;
  margin: 0 auto;
}

.subteams-section h2,
.leads-section h2,
.sponsors-section h2,
.contact-section h2 {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  text-align: center;
  font-size: 2rem;
  margin-bottom: 0.5rem;
}

.subteams-section .section-intro,
.leads-section .section-intro,
.sponsors-section .section-intro,
.contact-section .section-intro {
  text-align: center;
  color: var(--text-muted);
  max-width: 36rem;
  margin: 0 auto 3rem;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1.5rem;
}

.subteam-card {
  position: relative;
  background: var(--card-bg);
  border: 1px solid var(--border);
  overflow: hidden;
  transition: transform 0.2s ease, border-color 0.2s ease;
  background-image:
    radial-gradient(circle at 10px 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at 10px calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px);
}

.subteam-card.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

.subteam-card.reveal.in-view {
  opacity: 1;
  transform: translateY(0);
}

.subteam-card:hover {
  transform: translateY(-4px);
  border-color: var(--clarkson-gold);
}

.subteam-card:hover .spec-code {
  background: var(--hazard-gold);
  color: var(--hazard-black);
}

.spec-code {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--hazard-gold);
  background: rgba(5, 5, 5, 0.75);
  padding: 3px 9px 3px 7px;
  border-bottom-right-radius: 3px;
  transition: background 0.2s ease, color 0.2s ease;
}

.card-image-placeholder {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  background-image: radial-gradient(circle, var(--line-soft) 1px, transparent 1.4px);
  background-size: 16px 16px;
  background-color: var(--panel-raised);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.card-image {
  display: block;
  aspect-ratio: 16 / 9;
  width: 100%;
  object-fit: cover;
  object-position: center 65%;
}

.subteam-card h3 {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 0;
  padding: 1rem 1.1rem;
  font-size: 1.05rem;
}

/* ---------- Subteam leads ---------- */

.leads-section {
  padding: 4.5rem 2rem;
  max-width: 1200px;
  margin: 0 auto;
  border-top: 1px solid var(--line-soft);
}

/* Fixed 210px tracks rather than minmax(…, 1fr), and centered: a lead portrait
   is a person, so the plate has to stay portrait-proportioned instead of
   stretching to whatever width a flexible track hands it. Centering means the
   row reads deliberately while only one or two leads are up, and fills out
   left-to-right as the rest are added. auto-fit, not auto-fill: auto-fill keeps
   the empty tracks at full width, which leaves no free space for
   justify-content to center and strands a lone card at the left edge. */
.lead-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, 210px);
  justify-content: center;
  gap: 1.75rem;
}

/* The officers sit on their own row above the subteam leads. Every card is the
   same size — position and the role line carry the distinction, not scale. */
.lead-grid--officers {
  margin-bottom: 3rem;
}

.lead-card {
  position: relative;
  background: var(--card-bg);
  border: 1px solid var(--border);
  overflow: hidden;
  transition: transform 0.2s ease, border-color 0.2s ease;
  background-image:
    radial-gradient(circle at 10px 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at 10px calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px);
}

.lead-card:hover {
  transform: translateY(-4px);
  border-color: var(--clarkson-gold);
}

.lead-card:hover .spec-code {
  background: var(--hazard-gold);
  color: var(--hazard-black);
}

/* 4:5 rather than the cards' 16:9: a head-and-shoulders portrait wants the
   taller frame, and it keeps the leads visibly a different kind of card from
   the subteam plates above them. */
/* `height: auto` is load-bearing: the portraits carry width/height attributes
   for layout stability, and those map to presentational height hints that
   would otherwise pin the box to the file's own pixel height and leave
   aspect-ratio with nothing to do. */
.lead-portrait-placeholder,
.lead-portrait {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
}

.lead-portrait {
  object-fit: cover;
  object-position: center 30%;
}

.lead-portrait-placeholder {
  background-image: radial-gradient(circle, var(--line-soft) 1px, transparent 1.4px);
  background-size: 16px 16px;
  background-color: var(--panel-raised);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Reserved height, so every card is the same size whichever row it is in and
   whether or not it carries an officer role line — grid stretch only equalizes
   cards within one row, and the officers are a row of their own. Sized to the
   tallest case: name, role, and a subteam that wraps to two lines. */
.lead-caption {
  padding: 0.9rem 1.1rem 1rem;
  min-height: 8rem;
}

.lead-caption h3 {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 0;
  font-size: 1.05rem;
}

/* Officer title (Captain, Chief Engineer). Set in warm white so it reads as
   the stronger of the two label lines without spending gold on it — the gold
   here is the spec-code chip, and it stays rare. */
.lead-role {
  margin: 0.4rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}

/* The subteam is the lead's assignment, not their title, so it is set as a
   mono spec label under the name rather than as a second heading. */
.lead-subteam {
  margin: 0.35rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  transition: color 0.2s ease;
}

.lead-card:hover .lead-subteam {
  color: var(--clarkson-gold);
}

/* ---------- Lead detail page ---------- */

/* Portrait left, bio right, on the same 940px measure as the Our Car layout.
   The portrait column is fixed rather than fractional so the plate keeps its
   4:5 proportion whatever the bio length. */
.lead-layout {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: 2.5rem;
  align-items: start;
  max-width: 940px;
  margin: 0 auto;
  padding: 3.5rem 2rem 5rem;
}

.lead-layout .media-plate {
  margin: 0;
}

.lead-bio h2 {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: 1.5rem;
  margin: 0 0 1rem;
}

.lead-bio p {
  line-height: 1.6;
  color: var(--text);
  max-width: 62ch;
  margin: 0 0 1.1rem;
}

/* Year, major and outbound links, set above the bio so they line up with the
   top of the portrait. A hairline closes the block off from the bio the same
   way the home sections are ruled from each other. */
.lead-subheader {
  margin-bottom: 2rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--line-soft);
}

.lead-bio .lead-meta {
  margin: 0 0 1rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.lead-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (max-width: 760px) {
  .lead-layout {
    grid-template-columns: 1fr;
    gap: 1.75rem;
  }

  /* justify-self, not auto margins — a stretched grid item clamped by
     max-width keeps its auto margins resolved at 0. */
  .lead-layout .media-plate {
    max-width: 18rem;
    justify-self: center;
  }
}

/* ---------- Sponsors ---------- */

.sponsors-section {
  padding: 4.5rem 2rem 5rem;
  max-width: 1200px;
  margin: 0 auto;
  border-top: 1px solid var(--line-soft);
}

/* Flex rather than grid, and centered: every plate is the same width, so a
   short last row sits in the middle of the wall instead of hanging off the
   left edge the way a grid's leftover cells would. */
.sponsor-wall {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2rem 1.25rem;
}

/* One plate size for everyone. The clamp fits five across at full width and
   still keeps two to a row on a phone. */
.sponsor {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
  width: clamp(8rem, 16vw, 13rem);
  transition: transform 0.2s ease;
}

.sponsor:hover {
  transform: translateY(-3px);
}

/* An invisible frame, not a plate: it only reserves each logo the same 3:2
   footprint so the wall stays on a grid. Every mark sits straight on the ink
   ground with its own transparency intact. */
.sponsor-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 3 / 2;
  /* The frame is itself a column flex item, so its automatic minimum size is
     content-based: without this, a tall logo overrides the aspect-ratio and
     stands taller than the rest of its row. */
  min-height: 0;
  /* Breathing room inside each frame, so neighbouring logos (and their halos)
     never crowd each other. */
  padding: 0.75rem;
}

/* Sized to the frame's content box and letterboxed by object-fit rather than
   left at its intrinsic size: a tall logo given `max-height: 100%` still
   forces a flex container taller than its own aspect-ratio, which put one
   logo out of line with the rest of its row.

   Most sponsor marks are dark artwork — near-black wordmarks, deep red, navy —
   so on the ink ground each one is lit from behind instead of being boxed:
   zero-offset drop-shadows trace the logo's own alpha channel, so the halo
   follows the letterforms rather than a rectangle. Three stacked passes do
   different jobs — a tight opaque pass cuts the edge, a mid pass carries the
   glow, a wide faint pass keeps it from reading as a hard sticker outline.
   The light is Warm Plate White (#f3f0e6), never pure white. */
.sponsor-mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter:
    drop-shadow(0 0 1px rgba(243, 240, 230, 1))
    drop-shadow(0 0 3px rgba(243, 240, 230, 0.7))
    drop-shadow(0 0 12px rgba(243, 240, 230, 0.28));
  transition: filter 0.2s ease;
}

/* Hover warms the outermost pass to hazard gold, which is the same move the
   spec plates and the back link make. */
.sponsor:hover .sponsor-mark img {
  filter:
    drop-shadow(0 0 1px rgba(243, 240, 230, 1))
    drop-shadow(0 0 4px rgba(243, 240, 230, 0.8))
    drop-shadow(0 0 16px rgba(255, 205, 0, 0.45));
}

/* Held at two lines' height so a name that wraps at a narrow plate width can't
   shove its own plate up out of the row's shelf line; the first line of every
   name still starts at the same height either way. */
.sponsor-name {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  line-height: 1.35;
  min-height: 2.7em;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  text-align: center;
  transition: color 0.2s ease;
}

.sponsor:hover .sponsor-name {
  color: var(--clarkson-gold);
}

/* ---------- Contact (home, the band above the Join button) ---------- */

/* This band and the Join button below it read as one closing "get in touch"
   block, so the hairline that used to open the Join section moves up here and
   rules the pair off from the sponsor wall instead of splitting them. */
.contact-section {
  padding: 4rem 2rem 0;
  max-width: 1200px;
  margin: 0 auto;
  border-top: 1px solid var(--line-soft);
  text-align: center;
}

/* Tighter than the card sections' 3rem: here the intro leads straight into a
   single control rather than into a grid. */
.contact-section .section-intro {
  margin-bottom: 1.5rem;
}

/* Address and social marks on one line. align-items: stretch lets the two
   square icon buttons take their height from the address button beside them,
   so the three share a baseline and a height without any of it being hard-coded
   to a font size. Wraps rather than overflowing on a narrow phone. */
.contact-row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  justify-content: center;
  gap: 0.6rem;
}

/* The ruled secondary button, extended rather than reinvented (see DESIGN.md).
   Written as .cta-link.contact-email so it outranks the shared button rule
   further down the file, which would otherwise win on source order alone.

   The address keeps its own case — like a sponsor name or a caption it is data
   quoted verbatim, not a label the page gets to shout — and is set a step
   larger than a 0.8rem label so it can be read off the screen and typed by hand
   if the copy never happens. */
.cta-link.contact-email {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: 0;
  padding: 0.7rem 1.3rem;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  text-transform: none;
  cursor: pointer;
}

.contact-icon {
  flex: none;
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.3;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.75;
}

.contact-email:hover .contact-icon {
  opacity: 1;
}

.contact-social-group {
  display: flex;
  gap: 0.6rem;
}

/* Square siblings of the address button: same ruled border and gold hover, with
   the padding dropped so the mark centers in a box the row's height makes. */
.cta-link.contact-social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  /* Stretch only equalizes within a flex line, so once the row wraps a mark
     that lands alone on the second line would collapse to its glyph. The
     min-height keeps it square there. */
  min-height: 2.75rem;
  margin-top: 0;
  padding: 0;
}

.social-icon {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The lens dot on the Instagram mark and the stem dot on the "i" are solid in
   both real marks, so they are filled rather than stroked. */
.social-dot {
  fill: currentColor;
  stroke: none;
}

/* The confirmation sits in its own reserved strip below the row so it can
   appear and clear without moving the Join button underneath. */
.contact-note {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 1.5rem;
  margin-top: 0.85rem;
}

/* The chip is the spec-code device — flat gold on hazard black, the same stamp
   a card corner wears — not a floating notification surface. */
.contact-toast {
  opacity: 0;
  padding: 3px 9px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--hazard-black);
  background: var(--hazard-gold);
  transition: opacity 0.2s ease;
}

.contact-note.is-copied .contact-toast {
  opacity: 1;
}

@media (max-width: 520px) {
  .contact-section {
    padding: 3rem 1.25rem 0;
  }
  /* The address is the longest unbreakable string on the page; on a narrow
     phone the button gives up its padding before the address wraps. The row
     tightens with it so address and marks still share one line at 375px. */
  .cta-link.contact-email {
    padding: 0.7rem 0.9rem;
    font-size: 0.85rem;
  }
  .contact-row,
  .contact-social-group {
    gap: 0.45rem;
  }
  .cta-link.contact-social {
    width: 2.5rem;
    min-height: 2.5rem;
  }
  .social-icon {
    width: 17px;
    height: 17px;
  }
}

/* ---------- Join the team (home, last band before the footer) ---------- */

/* No top border — .contact-section carries the hairline for the pair. */
.join-section {
  padding: 2.5rem 2rem 5rem;
  max-width: 1200px;
  margin: 0 auto;
  text-align: center;
}

/* The one filled button in the system. Everything else is a ruled mono label
   (see .back-link / .cta-link) precisely so that this — the single place a
   visitor is asked to act — can be the thing that shouts. It stays inside the
   Hazard Accent Rule by shouting the way the rest of the site does: a flat
   gold fill on hazard black, the same device as a hovered spec code and the
   copy confirmation chip, never a gradient, tint or glow.
   Inline-level so the section's text-align centers it — <summary> is block. */
.join-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  padding: 1.05rem 2.5rem;
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--hazard-black);
  background: var(--hazard-gold);
  border: 1px solid var(--hazard-gold);
  cursor: pointer;
  list-style: none;
  transition: transform 0.2s ease, background 0.2s ease, color 0.2s ease;
}

.join-toggle::-webkit-details-marker {
  display: none;
}

/* Hover inverts the fill and lifts — the same two moves a spec plate makes,
   run in reverse. The open panel keeps the inverted state, so the button only
   shouts while the form behind it is still closed. */
.join-toggle:hover,
.join-panel[open] .join-toggle {
  background: transparent;
  color: var(--clarkson-gold);
}

.join-toggle:hover {
  transform: translateY(-3px);
}

.join-chevron {
  width: 14px;
  height: 9px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.2s ease;
}

.join-panel[open] .join-chevron {
  transform: rotate(180deg);
}

/* A spec plate like every other panel in the system: Panel ground, hairline
   border, rivet-dot corners, and a gold-on-black spec code in the corner. */
.join-plate {
  position: relative;
  /* Sized so the frame inside clears Google's own 640px design width once the
     plate's padding is taken off, rather than making the form reflow narrower. */
  max-width: 700px;
  margin: 1.75rem auto 0;
  padding: 2.4rem 1.75rem 1.75rem;
  text-align: left;
  background: var(--card-bg);
  border: 1px solid var(--border);
  background-image:
    radial-gradient(circle at 10px 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at 10px calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px);
  animation: join-unfold 0.35s cubic-bezier(0.16, 1, 0.3, 1);
}

/* <details> can't transition its own height, so the panel settles in instead —
   the same easing and offset the subteam plates use when they reach view. */
@keyframes join-unfold {
  from { opacity: 0; transform: translateY(-10px); }
  to { opacity: 1; transform: translateY(0); }
}

.join-intro {
  margin: 0 0 1.75rem;
  color: var(--text-muted);
  font-size: 0.95rem;
  line-height: 1.55;
}

/* The form is Google's, rendered on white and sized by Google. The plate's own
   padding is the dark frame around it, and a hairline keeps the white edge from
   meeting the ink ground raw. Width tracks the container so a phone gets the
   whole viewport; Google reflows the form to whatever width it is given. */
.join-embed {
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  border: 1px solid var(--line-soft);
  background: var(--panel-raised);
}

/* Empty until script.js builds the frame, and with JavaScript off it stays
   that way — so an empty bordered box never sits on the page. */
.join-embed:empty {
  display: none;
}

.join-embed iframe {
  display: block;
  width: 100%;
  border: 0;
}

/* Matches the outline a .media-embed gets: an embedded frame can take keyboard
   focus with an internal focus ring this page can't style cross-origin. */
.join-embed:focus-within {
  outline: 2px solid var(--clarkson-gold);
  outline-offset: 3px;
}

.join-fallback {
  display: block;
  width: fit-content;
  margin: 1.5rem auto 0;
}

@media (max-width: 520px) {
  .join-section {
    padding: 2rem 1.25rem 4rem;
  }
  .join-plate {
    padding: 2.4rem 0.75rem 1.5rem;
  }
}

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

.site-footer {
  text-align: center;
  padding: 2rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  border-top: 1px solid var(--line-soft);
}

/* ---------- Subteam placeholder page ---------- */

.subteam-hero {
  position: relative;
  padding: 8rem 2rem 4rem;
  text-align: center;
  background: linear-gradient(180deg, var(--bg-deep), var(--bg));
  border-bottom: 1px solid var(--line-soft);
  overflow: hidden;
}

.subteam-hero-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 68%;
  z-index: 1;
}

.subteam-hero.has-image {
  min-height: 26rem;
}

.subteam-hero.has-image::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(180deg, rgba(5, 15, 12, 0.5) 0%, rgba(5, 15, 12, 0.5) 70%, var(--bg) 100%);
}

.subteam-hero > .hero-tag,
.subteam-hero > h1,
.subteam-hero > p {
  position: relative;
  z-index: 3;
}

.subteam-hero h1 {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: clamp(1.8rem, 4vw, 2.75rem);
  margin: 0 0 0.5rem;
}

.subteam-hero p {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  letter-spacing: 0.01em;
  color: var(--text-muted);
  margin: 0;
}

.subteam-hero.has-image h1,
.subteam-hero.has-image p {
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.85), 0 4px 16px rgba(0, 0, 0, 0.7);
}

/* The system's one button treatment: a ruled-border mono label that goes gold
   on hover. Only the margin differs between the two uses — the back link is
   centered at the end of a page, the CTA sits inline under a paragraph. */
.back-link,
.cta-link,
.lead-links a {
  display: inline-block;
  padding: 0.6rem 1.2rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  border: 1px solid var(--line-strong);
  color: var(--text);
  transition: border-color 0.2s ease, color 0.2s ease;
}

.back-link {
  margin: 3rem auto;
}

.cta-link {
  margin-top: 1.6rem;
}

.back-link:hover,
.cta-link:hover,
.lead-links a:hover {
  border-color: var(--clarkson-gold);
  color: var(--clarkson-gold);
}

.placeholder-body {
  max-width: 700px;
  margin: 0 auto;
  padding: 3rem 2rem 6rem;
  text-align: center;
  color: var(--text-muted);
}

/* ---------- Subteam content pages (spec sheets, media plates) ---------- */

.hero-tag {
  display: inline-block;
  margin-bottom: 0.9rem;
  padding: 3px 9px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--hazard-black);
  background: var(--hazard-gold);
}

.content-section {
  max-width: 860px;
  margin: 0 auto;
  padding: 3rem 2rem;
}

/* Body copy keeps a 68ch reading measure, which is narrower than the section's
   own column. Centering the block splits the leftover space evenly instead of
   stacking it all on the right, where it read as a ragged gap beside the
   full-width headings and media plates. */
.content-section p {
  line-height: 1.6;
  color: var(--text);
  max-width: 68ch;
  margin-inline: auto;
}

.content-section + .content-section {
  border-top: 1px solid var(--line-soft);
}

.section-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-bottom: 1.5rem;
}

.section-header h2 {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: 1.6rem;
  margin: 0;
}

.rev-tag {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 10px;
  border: 1px solid var(--line-strong);
}

.rev-tag--current {
  color: var(--hazard-gold);
  border-color: var(--hazard-gold);
}

.rev-tag--legacy {
  color: var(--text-muted);
}

.spec-sheet {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem 1.5rem;
  margin: 0 0 2rem;
  padding: 1rem 1.25rem;
  background: rgba(8, 12, 10, 0.5);
  border: 1px solid var(--line-strong);
  font-family: var(--font-mono);
}

.spec-sheet > div {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.spec-sheet dt {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--hazard-gold);
}

.spec-sheet dd {
  margin: 0;
  font-size: 1rem;
  color: var(--text);
}

.media-plate {
  position: relative;
  margin: 2rem 0 0;
  padding: 0;
  border: 1px solid var(--line-soft);
  background: var(--panel);
  background-image:
    radial-gradient(circle at 10px 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) 10px, rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at 10px calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px),
    radial-gradient(circle at calc(100% - 10px) calc(100% - 10px), rgba(226, 232, 227, 0.4) 1.6px, transparent 2px);
}

.media-plate img {
  display: block;
  width: 100%;
  height: auto;
}

.media-embed {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
}

.media-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.media-embed:focus-within {
  outline: 2px solid var(--clarkson-gold);
  outline-offset: 3px;
}

.media-plate--crop img {
  aspect-ratio: 16 / 9;
  object-fit: cover;
  object-position: center 25%;
}

/* Per-photo crop framing. Every cropped image defaults to a band that suits
   most shots of its kind (`center 65%` on a card, `center 68%` on a hero,
   `center 25%` on a media plate). These two shift the band when a particular
   photo's subject sits somewhere else in its own frame; each is written
   against the rule it overrides so it wins on specificity. */

.card-image.crop-upper,
.subteam-hero-image.crop-upper {
  object-position: center 35%;
}

.card-image.crop-mid,
.subteam-hero-image.crop-mid,
.media-plate--crop img.crop-mid {
  object-position: center 47%;
}

.media-plate figcaption {
  padding: 0.7rem 1rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--text-muted);
  border-top: 1px solid var(--line-soft);
}

.fig-number {
  color: var(--hazard-gold);
  font-weight: 600;
  margin-right: 0.4em;
}

.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.5rem;
  margin-top: 2rem;
}

.media-grid .media-plate {
  margin-top: 0;
}

.media-grid .media-plate img {
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

/* Cutout plate: a background-removed part photo staged on a drafting field,
   the way a parts catalog shows a single component. The dot grid and the
   crossed datum lines do the work a drop shadow would otherwise be reached
   for, keeping the plate flat and line-drawn like the rest of the system. */

.media-plate--cutout .cutout-field {
  position: relative;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background-color: var(--panel-raised);
  background-image: radial-gradient(circle, var(--line-soft) 1px, transparent 1.4px);
  background-size: 16px 16px;
}

/* Positive stacking, matching .subteam-hero.has-image: datum lines at 1, the
   part at 2, so the lines always read as drafted underneath it. */
.media-plate--cutout .cutout-field::before,
.media-plate--cutout .cutout-field::after {
  content: "";
  position: absolute;
  z-index: 1;
  background: var(--hazard-gold);
  opacity: 0.35;
}

.media-plate--cutout .cutout-field::before {
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
}

.media-plate--cutout .cutout-field::after {
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
}

/* A cutout PNG carries a wide transparent margin that is rarely centered, so the
   part is placed by hand rather than by object-position. Measured on the ECU
   source (1800x2400): the opaque board spans x 3.1%-81.8%, y 20.8%-82.8% of the
   frame. Scaling the image to 68% of the field and offsetting by those bounds
   lands the board centered, filling ~84% of the plate's height. Re-measure and
   re-derive these three numbers if the image is ever replaced. */
.media-plate--cutout img {
  position: absolute;
  z-index: 2;
  width: 68%;
  height: auto;
  left: 21.1%;
  top: -20.5%;
}

/* The ECU board is close to square, so a landscape field height-limits it. On a
   phone that leaves the part too small to read, so the field squares up and the
   part is re-derived from the same bounds at a larger scale. */
@media (max-width: 640px) {
  .media-plate--cutout .cutout-field {
    aspect-ratio: 1 / 1;
  }

  .media-plate--cutout img {
    width: 88%;
    left: 12.6%;
    top: -10.8%;
  }
}
