/* ==================================================================
   Xiaoyi Tian Lab — editorial/academic treatment on the KSU palette.

   Brand rules being honoured (campus.kennesaw.edu brand guidelines):
     * black and gold are the primaries and should dominate;
     * gold text is only permitted on black backgrounds, so in light mode
       gold appears as rules, markers and active states — never as link
       text — and becomes a text colour only in dark mode;
     * grey #999 is the supporting neutral.

   Structure is carried by hairline rules and whitespace rather than
   cards, which is what makes it read as a journal page.
   ================================================================== */

/* --- type (families loaded via <link> in base.html) ------------------ */

:root {
  /* KSU's brand face is Proxima Nova (commercial, Adobe Fonts). It is
     named first so a licensed Adobe kit or a local install is used when
     available; Figtree is the openly-licensed stand-in we actually serve. */
  --font-display: "Proxima Nova", "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-sans: "Proxima Nova", "Figtree", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --wrap: 1080px;
  /* Single source of truth for the side gutter. Fluid so the page never
     runs to the edge on a phone or at a high browser zoom, where the
     centring margin has already collapsed to nothing. Change this one
     value to widen or tighten the margins site-wide. */
  --gutter: clamp(28px, 6vw, 72px);

  /* The gold rule that sits under a heading, KSU-style. One token so the
     site header, page headers and section heads stay in step. */
  --rule-gold: 5px solid var(--ksu-gold);

  /* KSU primaries */
  --ksu-gold: #ffd700;
  --ksu-black: #000000;
  --ksu-grey: #999999;

  /* Light mode: paper */
  --bg: #ffffff;
  --bg-subtle: #f7f6f3;
  --text: #11100e;
  --text-muted: #5f5c57;
  --text-faint: #767268;
  --rule: #e3e0da;
  --rule-strong: #11100e;
  --accent: #11100e;          /* link text — black, per brand */
  --accent-line: var(--ksu-gold); /* gold used as rule/marker */
  --marker-text: #11100e;     /* text sitting ON gold */

  /* Two grounds give the page its rhythm against the paper white. --surface
     is the panel ground: a neutral light grey rather than a tinted gold,
     because gold this dilute reads as a yellow cast instead of as brand. It
     is barely off the paper, so the block is carried by the rule that closes
     it and by the shift in ground, not by a colour competing with the type.
     Neutral rather than warm, so it stays a plain change of ground and adds
     no cast of its own. --surface-raised is the grey the small pieces (chips,
     link buttons) sit on, a step down so they stay visible inside a panel.

     Swap-ins: #fafafa (barely there) or #eeeeee (more present).
     Only this one value needs to change. */
  --surface: #f4f4f4;
  --surface-raised: #e9e9e9;

  /* The panel's inset. It doubles as the distance the panel bleeds back out
     past the text column, so the box grows while the content stays put —
     which is why it is a token and not a literal. Clamped against the gutter
     so the bleed can never reach the edge of a narrow screen. */
  --panel-pad: min(clamp(24px, 4vw, 56px), calc(var(--gutter) - 12px));
  /* How far the gold block behind a panel is thrown up and to the right. */
  --panel-offset: clamp(12px, 1.6vw, 22px);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0b0d0e;
    --bg-subtle: #14171a;
    --text: #f4f2ed;
    --text-muted: #a8a49c;
    --text-faint: #938e86;
    --rule: #262a2e;
    --rule-strong: #f4f2ed;
    --accent: var(--ksu-gold);  /* gold on black is brand-approved */
    --accent-line: var(--ksu-gold);
    --marker-text: #0b0d0e;
    /* Dark mode lifts a surface rather than darkening it, so the panel is a
       step above the page ground, keeping the same warm cast. */
    --surface: #17191b;
    --surface-raised: #1d2023;
  }
}

:root[data-theme="dark"] {
  --bg: #0b0d0e;
  --bg-subtle: #14171a;
  --text: #f4f2ed;
  --text-muted: #a8a49c;
  --text-faint: #938e86;
  --rule: #262a2e;
  --rule-strong: #f4f2ed;
  --accent: var(--ksu-gold);
  --accent-line: var(--ksu-gold);
  --marker-text: #0b0d0e;
  --surface: #17191b;
  --surface-raised: #1d2023;
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* A long email, URL or DOI must break rather than push the page wider
     than the screen. */
  overflow-wrap: break-word;
}

/* The gutter is the wrap's job. Anything that also sits on .wrap must set
   padding-block only, or it will wipe the gutter out.

   This column is also the site's measure. Text used to carry a second,
   narrower cap of its own (68ch), which broke leads, titles and body copy a
   line early while a third of the column beside them stayed empty; the
   column is what sets the line length now, and nothing re-caps it. */
.wrap {
  max-width: calc(var(--wrap) + var(--gutter) * 2);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

/* Headings are the serif voice. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.025em;
  margin: 0 0 .4em;
}
h1 { font-size: clamp(2.3rem, 5vw, 3.2rem); }
h2 { font-size: clamp(1.5rem, 2.6vw, 1.9rem); }
h3 { font-size: 1.2rem; }

p { margin: 0 0 1em; }

/* Links: black with a gold underline that thickens. Gold never carries
   the text itself in light mode. */
a {
  color: var(--accent);
  text-decoration: none;
  background-image: linear-gradient(var(--accent-line), var(--accent-line));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 2px;
  padding-bottom: 1px;
  transition: background-size .15s ease;
}
a:hover { background-size: 100% 6px; }
a:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 2px; }

img { max-width: 100%; height: auto; }

.skip-link {
  position: absolute; left: -9999px; top: 0;
  background: var(--ksu-gold); color: var(--ksu-black);
  padding: 10px 18px; z-index: 100; font-weight: 600; background-image: none;
}
.skip-link:focus { left: 0; }

.muted { color: var(--text-muted); }
.small { font-size: .875rem; }

/* --- header and footer ------------------------------------------------ */

/* The gold band under the masthead is KSU's most recognisable device
   (campus.kennesaw.edu), so it separates the header from the page here too —
   and, turned the other way up, the footer from the page below.

   Both are reversed bands: black under the light theme, white under the dark
   one. Rather than recolour brand, nav, toggle, columns and divider one by
   one, the palette tokens are redefined on the band itself — every descendant
   reads the flipped values through the cascade, and rules written against
   --text / --rule keep working unchanged. */
.site-header,
.site-footer {
  --bg: var(--ksu-black);
  --bg-subtle: #14171a;
  --text: #f4f2ed;
  --text-muted: #a8a49c;
  --text-faint: #938e86;
  --rule: #34383c;
  --rule-strong: #f4f2ed;
  --accent: var(--ksu-gold);
  /* The Quick links label: KSU gold on the black band. #ffd700 is 1.4:1 on
     white, so the white band uses its ordinary text colour instead and keeps
     the gold for the underline and the panel's accents. */
  --menu-ink: var(--ksu-gold);
  /* Nav links: the band's full-strength ink, pure black on the white band. */
  --nav-ink: var(--text);

  background: var(--bg);
  color: var(--text);
}

.site-header {
  border-bottom: var(--rule-gold);
  position: sticky; top: 0; z-index: 20;
}

/* …and the other way round once the page itself goes dark. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .site-header,
  :root:not([data-theme="light"]) .site-footer {
    --bg: #ffffff;
    --bg-subtle: #f7f6f3;
    --text: #11100e;
    --text-muted: #5f5c57;
    --text-faint: #767268;
    --rule: #d8d5cf;
    --rule-strong: #11100e;
    --accent: #11100e;
    --menu-ink: #11100e;
    --nav-ink: #000000;
  }
}
:root[data-theme="dark"] .site-header,
:root[data-theme="dark"] .site-footer {
  --bg: #ffffff;
  --bg-subtle: #f7f6f3;
  --text: #11100e;
  --text-muted: #5f5c57;
  --text-faint: #767268;
  --rule: #d8d5cf;
  --rule-strong: #11100e;
  --accent: #11100e;
  --menu-ink: #11100e;
  --nav-ink: #000000;
}
.site-header__inner {
  display: flex; align-items: center; gap: 20px;
  min-height: 72px; flex-wrap: wrap;
}
.site-header__brand {
  font-family: var(--font-display);
  font-size: 1.3rem; font-weight: 600; letter-spacing: -0.015em;
  color: var(--text); background-image: none; margin-right: auto;
}
.site-header__brand:hover { color: var(--text); }

.site-nav { display: flex; gap: 20px; flex-wrap: wrap; }
.site-nav a {
  font-size: .82rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em;
  color: var(--nav-ink);
  background-image: none; padding: 6px 0;
  border-bottom: 2px solid transparent;
}
.site-nav a:hover { border-bottom-color: var(--rule); }
.site-nav a[aria-current="page"] { border-bottom-color: var(--ksu-gold); }

/* --- footer ------------------------------------------------------------ */

/* KSU's footer: the gold rule opening it, the university lockup and the
   social marks across the top, then columns of links closed by a thin legal
   bar. It takes its palette from the reversed-band block above, so it stays
   the mirror of the masthead in either theme. */
.site-footer {
  margin-top: 96px;
  border-top: var(--rule-gold);
  color: var(--text-muted);
  font-size: .9rem;
  /* KSU prints a faint chevron watermark across the band. Reproduced as wide
     diagonal stripes mixed out of --text rather than a fixed white, so they
     stay equally faint whichever way the band is facing. Kept at the edge of
     visible: any stronger and the stripes start competing with the columns
     instead of giving the band a surface. */
  background-image: repeating-linear-gradient(
    115deg,
    transparent 0 100px,
    color-mix(in srgb, var(--text) 2%, transparent) 100px 200px
  );
}

/* padding-block only: .site-footer__inner rides on .wrap. */
.site-footer__inner { padding-block: clamp(34px, 5vw, 52px) 26px; }

.site-footer__top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px 32px; flex-wrap: wrap;
  padding-bottom: 32px;
}
.site-footer__ksu { display: inline-flex; background-image: none; }
/* Larger than the masthead's: down here the lockup is the identity of the
   page rather than a companion to the lab name. */
.site-footer .ksu-logo { height: 52px; }

.site-footer__social {
  display: flex; flex-wrap: wrap; gap: 10px;
  list-style: none; margin: 0; padding: 0;
}
.site-footer__social a {
  display: grid; place-items: center;
  width: 40px; height: 40px;
  color: var(--text-muted);
  background-image: none;
  border: 1px solid var(--rule);
  transition: color .15s ease, background-color .15s ease, border-color .15s ease;
}
/* Gold ground with a black mark reads correctly on both faces of the band,
   so the one hover state serves either theme. */
.site-footer__social a:hover {
  color: var(--ksu-black);
  background-color: var(--ksu-gold);
  border-color: var(--ksu-gold);
}
.social-icon { display: block; width: 19px; height: 19px; }

.site-footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 34px 44px;
  padding-block: 34px;
  border-top: 1px solid var(--rule);
}
.site-footer__col h2 {
  font-size: .78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .11em;
  color: var(--text);
  margin: 0 0 16px;
}
/* The short gold bar under a footer heading, KSU-style — fixed width rather
   than the full-width rule the page headings carry, which would read as a
   divider between the columns. */
.site-footer__col h2::after {
  content: "";
  display: block;
  width: 44px; height: 4px;
  background: var(--ksu-gold);
  margin-top: 10px;
}

.site-footer__address {
  font-style: normal; line-height: 1.65; margin: 0 0 14px;
}
.site-footer__strong { display: block; color: var(--text); font-weight: 600; }
.site-footer__office { display: block; }

.site-footer__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 10px;
}
/* Footer links drop the gold underline the body copy carries — three columns
   of permanently underlined text would be a wall of gold — and pick it up on
   hover instead. */
.site-footer__list a {
  color: var(--text-muted);
  background-image: none;
  border-bottom: 1px solid transparent;
}
.site-footer__list a:hover { color: var(--text); border-bottom-color: var(--ksu-gold); }

.site-footer__bottom {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px 28px; flex-wrap: wrap;
  border-top: 1px solid var(--rule);
  padding-top: 22px;
  font-size: .82rem;
}
.site-footer__copyright { margin: 0; color: var(--text-faint); }
.site-footer__legal {
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  list-style: none; margin: 0; padding: 0;
}
.site-footer__legal a {
  color: var(--text-faint);
  background-image: none;
  border-bottom: 1px solid transparent;
}
.site-footer__legal a:hover { color: var(--text); border-bottom-color: var(--ksu-gold); }

@media (max-width: 640px) {
  /* Left-aligned once the row can wrap: space-between would otherwise strand
     the social marks against the right edge on a line of their own. */
  .site-footer__top { justify-content: flex-start; padding-bottom: 26px; }
  .site-footer .ksu-logo { height: 40px; }
  .site-footer__cols { gap: 30px; padding-block: 30px; }
  /* The legal bar sits under the copyright rather than beside it once the
     row can no longer hold both without splitting the link list awkwardly. */
  .site-footer__bottom { justify-content: flex-start; }
}

/* --- page furniture --------------------------------------------------- */

/* padding-block only: .page rides on .wrap, whose gutter must survive. */
.page { padding-block: clamp(32px, 6vw, 56px) 0; }

.page__header {
  margin-bottom: 44px;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--rule);
}

/* KSU sets a gold bar below a heading rather than ruling the whole column
   (campus.kennesaw.edu). The full-width gold band stays reserved for the
   masthead, so the two never compete.

   fit-content shrinks the heading box to its text so the bar can span it
   exactly; it is self-clamping, so a long heading still wraps instead of
   overflowing, and no max-width is needed here (setting one would trample
   the ch-based limits some of these headings already carry). */
.page__header h1,
.intro-block h1,
.section__head h2,
.people-group > h2,
.pub-year { width: fit-content; }

.page__header h1::after,
.intro-block h1::after,
.section__head h2::after,
.people-group > h2::after,
.pub-year::after {
  content: "";
  display: block;
  width: 100%; height: 5px;
  background: var(--ksu-gold);
  margin-top: 10px;
}
/* The eyebrow-style labels are small type, so the bar sits closer. */
.people-group > h2::after,
.pub-year::after { margin-top: 8px; }
/* The lead is a sentence or two, not a column of prose, so it takes the page
   column like the title and the rule above it rather than the reading
   measure — which was breaking a lead that had 280px of empty column left. */
.page__intro {
  font-family: var(--font-display);
  font-size: 1.2rem; line-height: 1.55;
  color: var(--text-muted);
}
.page__intro p:last-child { margin-bottom: 0; }

.hero { padding: 88px 0 56px; border-bottom: 1px solid var(--rule); }
.hero h1 { max-width: 16ch; }
.hero p {
  font-family: var(--font-display);
  font-size: 1.3rem; line-height: 1.5;
  color: var(--text-muted); max-width: 58ch;
}

.section { padding-block: clamp(40px, 6vw, 72px) 0; }

/* A tinted section is full-bleed: on the homepage these sit outside .wrap
   and carry a .wrap of their own, so the band runs edge to edge while the
   text stays on the same column as everything else. The margin-top is what
   separates the band from the untinted section above it, whose own padding
   is top-only. */
/* A panel is a section lifted onto the grey ground: square corners, no frame,
   and the gold rule closing the bottom edge alone — the same --rule-gold the
   masthead and the headings carry, so the block closes on brand. Sections
   alternate between panelled and plain, which is what gives the page its
   rhythm.

   The box is wider than the text column: the negative inline margin and the
   inline padding are the same token, so the panel bleeds out by exactly the
   amount it insets its contents and every line still starts on the column the
   unpanelled sections use. */
.panel {
  background: var(--surface);
  border-bottom: var(--rule-gold);
  border-radius: 0;
  padding: clamp(28px, 4vw, 48px) var(--panel-pad);
  /* Room above for the gold block, and the right bleed given back to it, so
     the block lands inside the budget the panel already had rather than
     reaching any closer to the edge of the screen. */
  margin-block-start: var(--panel-offset);
  margin-inline: calc(-1 * var(--panel-pad))
                 calc(var(--panel-offset) - var(--panel-pad));
  position: relative;
}

/* KSU's offset block: a gold rectangle thrown behind the panel and up to the
   right, so all that shows is an L of gold along the top and right edges.

   z-index below zero puts it behind the panel's own background — which only
   works while .panel creates no stacking context of its own, so it must keep
   z-index auto and stay free of opacity, filter and transform. */
.panel::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: calc(-1 * var(--panel-offset));
  right: calc(-1 * var(--panel-offset));
  width: min(44%, 420px);
  height: min(72%, 300px);
  background: var(--ksu-gold);
}

.section__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px 24px; margin-bottom: 24px; flex-wrap: wrap;
  padding-bottom: 10px;
}
.section__head h2 { margin: 0; }
/* The "see everything" link is a button now, so only plain links left in a
   section head take the small uppercase treatment. */
.section__head a:not(.btn) {
  font-size: .82rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em;
}

/* Small uppercase label used above groups. */
.eyebrow {
  font-size: .74rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em;
  color: var(--text-faint);
}

/* --- filters: a toolbar, not a boxed panel ---------------------------- */

.filters {
  padding-bottom: 20px; margin-bottom: 32px;
  border-bottom: 1px solid var(--rule);
}
.filters__row { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-end; }
/* Each field grows to share the row but may shrink past its ideal width,
   so a narrow screen gets one field per line instead of an overflow. */
.filters__row > div { flex: 1 1 170px; min-width: 0; }
.filters label {
  display: block; font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-faint); margin-bottom: 5px;
}
.filters input[type="search"],
.filters input[type="text"],
.filters select {
  font-family: var(--font-sans); font-size: .95rem;
  padding: 7px 2px; width: 100%; min-width: 0;
  color: var(--text); background: transparent;
  border: 0; border-bottom: 1px solid var(--rule-strong);
  border-radius: 0;
}
.filters select { padding-right: 18px; }
.filters input:focus, .filters select:focus {
  outline: none; border-bottom-color: var(--ksu-gold); border-bottom-width: 2px;
}
.filters input::placeholder { color: var(--text-faint); }

/* Buttons are pills — the shape KSU puts its calls to action in. Gold ground
   with black text is the primary; the quieter variants are the same pill at
   lower volume, so a page can carry several without them competing. */
.btn {
  font-family: var(--font-sans); font-size: .78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; line-height: 1.2;
  cursor: pointer; padding: 12px 26px; border-radius: 999px;
  border: 1px solid transparent;
  background: var(--ksu-gold); color: var(--ksu-black);
  background-image: none;
  display: inline-flex; align-items: center; gap: .55em;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
/* Black fill with a gold ring: the ring is what keeps the hovered state
   visible once the page itself goes dark. */
.btn:hover {
  background: var(--ksu-black); color: var(--ksu-gold); border-color: var(--ksu-gold);
}
.btn:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 3px; }

/* The secondary action — "clear filters" — stays the square, unfilled
   rectangle it has always been: the page's own ground inside a hairline,
   so it never competes with the gold pill it sits beside. It overrides the
   pill radius and padding .btn sets rather than inheriting them. */
.btn--ghost {
  background: transparent; color: var(--text); border-color: var(--rule-strong);
  border-radius: 0; padding: 9px 20px;
}
.btn--ghost:hover { background: var(--rule-strong); color: var(--bg); }

.btn--sm { font-size: .72rem; padding: 8px 18px; }

/* The way back to the index. It was set as faint grey body text, which read
   as a caption rather than as the control it is, so it borrows the ghost
   button: the page's own ground inside a hairline, with the arrow pulling
   left on hover the way the forward buttons push right. */
.back-link { margin-bottom: 26px; }
.back-link__arrow { transition: transform .15s ease; }
.back-link:hover .back-link__arrow { transform: translateX(-3px); }

.btn__arrow { transition: transform .15s ease; }
.btn:hover .btn__arrow { transform: translateX(3px); }

/* keyword chips */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
/* Chips are miniature pills, so they read as the same family as the buttons.
   Grey ground, never gold, until one is actually selected. */
.chip {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .78rem; font-weight: 500; padding: 4px 12px;
  border: 1px solid var(--rule); border-radius: 0;
  color: var(--text-muted); background-image: none; white-space: nowrap;
  max-width: 100%;
}
/* A tag longer than the screen has to be allowed to break. */
@media (max-width: 520px) { .chip { white-space: normal; } }
.chip:hover { color: var(--text); border-color: var(--rule-strong); }
.chip--on {
  background: var(--ksu-gold); border-color: var(--ksu-gold);
  color: var(--marker-text); font-weight: 700;
}
.chip__count { opacity: .6; font-variant-numeric: tabular-nums; font-size: .72rem; }

.result-count {
  color: var(--text-faint); font-size: .78rem;
  text-transform: uppercase; letter-spacing: .1em; font-weight: 600;
  margin-bottom: 24px;
}

.empty {
  padding: 64px 0; text-align: center; color: var(--text-muted);
  border-top: 1px solid var(--rule);
}

/* --- people ----------------------------------------------------------- */

.people-group { margin-bottom: 56px; }
.people-group > h2 {
  font-family: var(--font-sans);
  font-size: .74rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em;
  color: var(--text-faint);
  margin-bottom: 28px;
}
/* min() so the track can fall below its ideal width rather than overflow
   the viewport on a narrow screen. */
.people-grid {
  display: grid; gap: 34px 28px;
  grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr));
}
.person-card__photo {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: 50%;
  background: var(--bg-subtle); margin-bottom: 16px;
}
.person-card__photo--blank {
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  font-family: var(--font-display); color: var(--text-faint);
  font-size: 2.6rem; font-weight: 700;
}
.person-card__name {
  font-family: var(--font-display); font-size: 1.15rem; font-weight: 600;
  line-height: 1.25; margin-bottom: 2px;
}
.person-card__name a { background-image: none; }
.person-card__name a:hover {
  background-image: linear-gradient(var(--accent-line), var(--accent-line));
  background-size: 100% 2px;
}
.person-card__meta { font-size: .85rem; color: var(--text-muted); line-height: 1.4; }

.person-detail { display: grid; grid-template-columns: 260px 1fr; gap: 52px; align-items: start; }
.person-detail__photo { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 50%; }
.person-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.person-bio p { font-family: var(--font-display); font-size: 1.08rem; line-height: 1.65; }

/* The photo column is a fixed 260px, so everything the viewport loses comes
   out of the text beside it. Below this the remainder is too narrow for the
   publication list that sits in it — the titles, authors and venues were
   breaking over four and five lines in a ~200px column while the space under
   the portrait stood empty — so the two stack and the list gets the page. */
@media (max-width: 900px) {
  .person-detail { grid-template-columns: 1fr; gap: 28px; }
  .person-detail__photo { max-width: 200px; }
}

/* --- projects: no cards, just rules ----------------------------------- */

.project-grid {
  display: grid; gap: 44px 36px;
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
.project-card { display: flex; flex-direction: column; }
/* The image is a second, mouse-only route to the project. It carries the
   card's bottom margin so the anchor collapses to exactly the image box and
   the click target never runs past it, and it drops the underline the global
   link rule paints. */
.project-card__media {
  display: block; margin-bottom: 16px;
  background-image: none;
}
.project-card__media .project-card__img { margin-bottom: 0; }
.project-card__media:hover .project-card__img { opacity: .88; }
.project-card__img {
  width: 100%; aspect-ratio: 16/9; object-fit: cover;
  background: var(--bg-subtle); margin-bottom: 16px;
  transition: opacity .18s ease;
}
.project-card__body { display: flex; flex-direction: column; gap: 10px; }
.project-card__title { font-size: 1.3rem; margin: 0; }
.project-card__title a { background-image: none; }
.project-card__title a:hover {
  background-image: linear-gradient(var(--accent-line), var(--accent-line));
  background-size: 100% 2px;
}
.project-card__summary {
  color: var(--text-muted); font-size: .95rem; margin: 0; max-width: 46ch;
}

/* Credits and links on a project page: hairline rows, no card, so the facts
   sit with the projects treatment rather than borrowing the contact panel. */
.project-facts {
  margin: 28px 0 36px;
  border-top: 1px solid var(--rule);
}
.project-facts__row {
  display: grid; grid-template-columns: 92px minmax(0, 1fr);
  gap: 12px; padding: 11px 0;
  border-bottom: 1px solid var(--rule);
}
.project-facts__row dt {
  font-family: var(--font-sans);
  font-size: .7rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-faint);
  padding-top: .22em;
}
.project-facts__row dd {
  margin: 0; font-size: .95rem; line-height: 1.5;
  overflow-wrap: anywhere;
}
@media (max-width: 480px) {
  /* The label column starves the value on a phone; stack instead. */
  .project-facts__row { grid-template-columns: 1fr; gap: 4px; }
}

/* Several facts to a row — grants, partner organisations — stay a list
   rather than a comma-joined string, so each one keeps its own link. */
.fact-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 7px;
}
.fact-list li { line-height: 1.45; }
.fact-list__note { color: var(--text-muted); font-size: .85rem; }
/* Chips inside a facts row line up with the label instead of hanging below it. */
.chips--tight { margin-top: 1px; gap: 6px; }

/* --- project pictures -------------------------------------------------
   One picture is a plain figure. Several become a collage: a six-column
   grid with fixed-height rows, so the cells tile instead of each image
   setting its own height. The count modifier on the container decides the
   shape — pairs split in half, threes run across, five or more get a
   feature image with a mosaic beside it — and the images crop to fill. */

.project-figure { margin: 28px 0 0; }
.project-figure img { display: block; width: 100%; height: auto; }
.project-figure figcaption,
.collage__item figcaption {
  font-size: .8rem; color: var(--text-muted); line-height: 1.4; margin-top: 8px;
}

.collage {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-auto-rows: clamp(72px, 9vw, 112px);
  grid-auto-flow: dense;
  gap: 12px;
  margin: 28px 0 0;
}
.collage__item {
  grid-column: span 3; grid-row: span 2;
  margin: 0; min-width: 0;
  display: flex; flex-direction: column;
}
/* The anchor takes the leftover height so a caption shortens the picture
   rather than pushing it out of its cell. */
.collage__item a { display: block; flex: 1; min-height: 0; background-image: none; }
.collage__item img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; background: var(--bg-subtle);
  transition: opacity .18s ease;
}
.collage__item a:hover img { opacity: .88; }

.collage--3 .collage__item { grid-column: span 2; }
.collage--many .collage__item { grid-column: span 2; }
.collage--many .collage__item:first-child { grid-column: span 4; grid-row: span 4; }

@media (max-width: 640px) {
  /* Six columns starve every cell on a phone; drop to a pair, with the
     feature image running the full width. */
  .collage { grid-template-columns: repeat(2, 1fr); grid-auto-rows: clamp(84px, 22vw, 128px); }
  .collage__item,
  .collage--3 .collage__item,
  .collage--many .collage__item { grid-column: span 1; grid-row: span 2; }
  .collage--many .collage__item:first-child { grid-column: span 2; grid-row: span 2; }
}

.badge {
  display: inline-block; font-size: .68rem; font-weight: 700;
  letter-spacing: .11em; text-transform: uppercase;
  padding: 3px 9px; background: var(--ksu-gold); color: var(--marker-text);
}
.badge--active,
.badge--planned { border: 1px solid #c9a800; }
.badge--completed { background: #e4e4e4; color: #333333; border: 1px solid #b5b5b5; }
a.badge--active:hover,
a.badge--planned:hover { border-color: #7a6600; }
a.badge--completed:hover { border-color: #555555; }
/* Status badge + year range: centred as a flex row so the padded badge
   doesn't hang below the year's text baseline (same fix as .news-meta). */
.status-line {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-variant-numeric: tabular-nums;
}
/* Status badges on project cards link to that status filter. */
a.badge { background-image: none; }
a.badge:hover { text-decoration: underline; text-underline-offset: 2px; }

/* --- publications ----------------------------------------------------- */

.pub-year {
  font-family: var(--font-sans);
  font-size: .78rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-faint);
  margin: 48px 0 0;
}
.pub-year:first-of-type { margin-top: 0; }

.pub {
  padding: 22px 0; border-bottom: 1px solid var(--rule);
  display: grid; grid-template-columns: 1fr auto; gap: 6px 32px;
}
/* No measure here. A title is scanned, not read in long lines, and .pub is a
   grid whose first column already stops it short of the links beside it —
   a 52ch cap on top of that only broke titles over three lines while a third
   of the column stood empty. */
.pub__title {
  font-family: var(--font-display);
  font-size: 1.2rem; font-weight: 500; line-height: 1.3;
  margin: 0;
}
.pub__title a { background-image: none; }
.pub__title a:hover {
  background-image: linear-gradient(var(--accent-line), var(--accent-line));
  background-size: 100% 2px;
}
.pub__authors { font-size: .88rem; color: var(--text-muted); margin: 6px 0 0; }
.pub__authors a { background-image: none; color: var(--text); font-weight: 600; }
.pub__authors a:hover {
  background-image: linear-gradient(var(--accent-line), var(--accent-line));
  background-size: 100% 2px;
}
.pub__venue {
  font-family: var(--font-display); font-style: italic;
  font-size: .95rem; color: var(--text-muted); margin: 3px 0 0;
}
.pub__actions {
  display: flex; flex-direction: column; align-items: flex-end;
  gap: 8px; white-space: nowrap;
}
.pub__links { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.pub__links a {
  font-size: .68rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  border: 1px solid var(--rule); padding: 3px 9px;
  color: var(--text-muted); background-image: none;
}
.pub__links a:hover {
  background: var(--ksu-gold); border-color: var(--ksu-gold); color: var(--marker-text);
}
.pub__cites {
  font-size: .72rem; font-weight: 600; letter-spacing: .06em;
  color: var(--text-faint); font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}

.pub__abstract, .bibtex { grid-column: 1 / -1; margin: 12px 0 0; }
.pub__abstract summary, .bibtex summary {
  cursor: pointer; font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; color: var(--text-faint);
  list-style: none;
}
.pub__abstract summary::-webkit-details-marker,
.bibtex summary::-webkit-details-marker { display: none; }
.pub__abstract summary::before, .bibtex summary::before { content: "+ "; }
details[open] > summary::before { content: "\2212 "; }
.pub__abstract summary:hover, .bibtex summary:hover { color: var(--text); }
.pub__abstract p {
  font-family: var(--font-display); font-size: 1rem; line-height: 1.6;
  color: var(--text-muted); margin: 10px 0 0;
}
.bibtex pre {
  font-family: var(--font-mono); font-size: .78rem; line-height: 1.55;
  background: var(--bg-subtle); border-left: 3px solid var(--ksu-gold);
  padding: 16px; overflow-x: auto; margin: 10px 0 0;
}

@media (max-width: 640px) {
  .pub { grid-template-columns: 1fr; }
  .pub__actions { flex-direction: row; align-items: center; justify-content: flex-start; }
}

/* --- news ------------------------------------------------------------- */

/* Like the publication list, news is a ruled column rather than a grid of
   cards: a date line, a headline and a summary, separated by hairlines. An
   item with a picture splits into a fixed thumbnail column and the text. */
.news-list { border-top: 1px solid var(--rule); }
.news-item {
  padding: 24px 0;
  border-bottom: 1px solid var(--rule);
}
.news-item--with-media {
  display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 24px;
}
/* The thumbnail is a second, mouse-only route to the item. It collapses to
   exactly the image box so the click target never runs past it, and drops
   the underline the global link rule paints. */
.news-item__media { display: block; background-image: none; }
.news-item__img {
  width: 100%; aspect-ratio: 3/2; object-fit: cover;
  background: var(--bg-subtle); display: block;
  transition: opacity .18s ease;
}
.news-item__media:hover .news-item__img { opacity: .88; }
.news-item__body { min-width: 0; }
/* The date and the type badge share a line, in both the list and the page
   for a single item. Flex rather than inline text: the badge is a box with
   its own padding, so on a text baseline it hangs low against the date and
   sits tight against it. Centring the two and setting an explicit gap is
   what lines them up. Tabular figures keep the dates aligned down the list. */
.news-item__meta,
.news-meta {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 8px;
  font-family: var(--font-sans);
  font-size: .74rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.news-item__title {
  font-family: var(--font-display);
  font-size: 1.25rem; font-weight: 500; line-height: 1.3;
  margin: 0; max-width: 52ch;
}
.news-item__title a { background-image: none; }
.news-item__title a:hover {
  background-image: linear-gradient(var(--accent-line), var(--accent-line));
  background-size: 100% 2px;
}
/* An item with a thumbnail is narrowed by its own grid column; one without
   takes the row. */
.news-item__summary {
  color: var(--text-muted); font-size: .95rem; line-height: 1.6;
  margin: 8px 0 0;
}
/* On the homepage the list is a taster inside a panel, so the last hairline
   would double up with the panel's own gold rule. */
.news-list--compact .news-item:last-child { border-bottom: 0; padding-bottom: 0; }

.news-hero__img {
  width: 100%; height: auto; display: block;
  margin: 0 0 28px; background: var(--bg-subtle);
}
.news-source { margin: 28px 0 0; }

/* --- pagination -------------------------------------------------------- */

.pager {
  display: flex; align-items: center; justify-content: center;
  gap: 16px; flex-wrap: wrap;
  margin-top: 36px; padding-top: 24px;
  border-top: 1px solid var(--rule);
}
.pager__status {
  font-family: var(--font-sans);
  font-size: .74rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
  /* The thumbnail column starves the headline on a phone; stack instead. */
  .news-item--with-media { grid-template-columns: 1fr; gap: 14px; }
  .news-item__img { aspect-ratio: 16/9; }
}

/* --- theme toggle ------------------------------------------------------ */

.theme-toggle {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer;
  background: transparent; color: var(--text-muted);
  border: 1px solid var(--rule); border-radius: 50%;
  transition: color .15s, border-color .15s;
}
.theme-toggle:hover { color: var(--text); border-color: var(--ksu-gold); }
.theme-toggle:focus-visible { outline: 2px solid var(--ksu-gold); outline-offset: 2px; }

.theme-toggle__icon {
  position: relative; width: 15px; height: 15px;
  border-radius: 50%; background: currentColor;
  transition: transform .3s ease;
}
.theme-toggle__icon::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: var(--bg); transform: translate(30%, -30%);
  transition: transform .3s ease, opacity .2s ease;
}
.theme-toggle[aria-pressed="true"] .theme-toggle__icon {
  transform: scale(.62);
  box-shadow:
    0 -11px 0 -4.5px currentColor,  0 11px 0 -4.5px currentColor,
    -11px 0 0 -4.5px currentColor,  11px 0 0 -4.5px currentColor,
    -8px -8px 0 -5px currentColor,  8px 8px 0 -5px currentColor,
    -8px 8px 0 -5px currentColor,   8px -8px 0 -5px currentColor;
}
.theme-toggle[aria-pressed="true"] .theme-toggle__icon::after {
  transform: translate(0, 0); opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; }
}

/* --- banner carousel --------------------------------------------------- */

.banner {
  position: relative;
  background: var(--ksu-black);
  overflow: hidden;
  /* Closed by the same gold rule the masthead sits on, so the slideshow reads
     as a band bracketed top and bottom rather than as a photo that runs out
     into the page. */
  border-bottom: var(--rule-gold);
}
.banner__track { position: relative; }
.banner__slide {
  position: absolute; inset: 0;
  opacity: 0; visibility: hidden;
  transition: opacity .7s ease, visibility .7s;
}
.banner__slide.is-active { position: relative; opacity: 1; visibility: visible; }
.banner__img {
  display: block; width: 100%;
  aspect-ratio: 21/9; object-fit: cover;
  /* Darkened so overlaid text stays legible over any uploaded photo. */
  filter: brightness(.62);
}
.banner__overlay {
  position: absolute; inset: auto 0 0 0;
  padding: 0 0 clamp(28px, 5vw, 64px);
  background: linear-gradient(transparent, rgba(0, 0, 0, .55));
}
/* These two keep a cap, unlike the rest: they are set over the picture, and
   a line that runs the full width of the banner reads across the subject of
   the photo. The caps are sized so a heading and a caption of ordinary length
   still land on one line — 20ch and 54ch were clipping both a word early. */
.banner__heading {
  font-size: clamp(1.8rem, 4.2vw, 3.1rem);
  color: #fff; margin: 0 0 .25em; max-width: 26ch;
  text-shadow: 0 2px 18px rgba(0, 0, 0, .5);
}
.banner__caption {
  font-size: clamp(1rem, 1.6vw, 1.2rem);
  color: rgba(255, 255, 255, .92); max-width: 64ch; margin: 0;
  text-shadow: 0 1px 12px rgba(0, 0, 0, .5);
}
.banner__cta { margin-top: 20px; }

.banner__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 46px; height: 46px; display: flex; align-items: center; justify-content: center;
  font-size: 1.6rem; line-height: 1; cursor: pointer;
  color: #fff; background: rgba(0, 0, 0, .35);
  border: 1px solid rgba(255, 255, 255, .35); border-radius: 50%;
}
.banner__nav:hover { background: var(--ksu-gold); color: var(--ksu-black); border-color: var(--ksu-gold); }
.banner__nav:focus-visible { outline: 3px solid var(--ksu-gold); outline-offset: 2px; }
.banner__nav--prev { left: 16px; }
.banner__nav--next { right: 16px; }

.banner__dots {
  position: absolute; left: 0; right: 0; bottom: 14px;
  display: flex; justify-content: center; gap: 9px;
}
.banner__dots button {
  width: 30px; height: 4px; padding: 0; cursor: pointer;
  background: rgba(255, 255, 255, .45); border: 0;
  transition: background .2s;
}
.banner__dots button[aria-selected="true"] { background: var(--ksu-gold); }
.banner__dots button:focus-visible { outline: 2px solid var(--ksu-gold); outline-offset: 3px; }

.banner__pause {
  position: absolute; right: 16px; bottom: 14px;
  width: 28px; height: 28px; cursor: pointer;
  background: rgba(0, 0, 0, .35); color: #fff;
  border: 1px solid rgba(255, 255, 255, .3); border-radius: 50%;
}
.banner__pause::before {
  content: ""; display: block; margin: 0 auto;
  width: 8px; height: 9px;
  border-left: 3px solid currentColor; border-right: 3px solid currentColor;
}
.banner__pause[aria-pressed="true"]::before {
  width: 0; height: 0; border: 0;
  border-left: 9px solid currentColor;
  border-top: 5px solid transparent; border-bottom: 5px solid transparent;
}

@media (max-width: 640px) {
  .banner__img { aspect-ratio: 4/3; }
  .banner__nav { width: 38px; height: 38px; }
}

/* --- homepage blocks --------------------------------------------------- */

.intro-block { padding: clamp(48px, 7vw, 88px) 0 8px; }
/* The lab's full name is long, so no ch-based cap here and a gentler ramp
   than the global h1: sized to sit on one line for the width of the column,
   it breaks only once the viewport itself is too narrow to hold it. The vw
   term tracks the column (.wrap is 88vw until it caps at 1080px), so the
   break happens at about 780px rather than at some arbitrary character count.
   `balance` then splits the lines evenly instead of 90/10. */
.intro-block h1 {
  font-size: clamp(1.7rem, 3.55vw, 2.75rem);
  text-wrap: balance;
}
.intro-block__tagline {
  font-size: clamp(1.15rem, 2vw, 1.45rem);
  font-weight: 500; color: var(--text-muted);
  max-width: 56ch; margin: 0 0 1.2em;
}
.intro-block__body { font-size: 1.05rem; }

.areas {
  display: grid; gap: 32px;
  grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
}
.area { border-top: 3px solid var(--ksu-gold); padding-top: 16px; }
.area__num {
  display: block; font-size: .72rem; font-weight: 700;
  letter-spacing: .14em; color: var(--text-faint); margin-bottom: 8px;
}
.area__title { font-size: 1.15rem; margin: 0 0 .4em; }
.area__desc { color: var(--text-muted); font-size: .95rem; margin: 0; }

.prose p { font-size: 1.05rem; }

/* --- join us: text beside a picture ------------------------------------ */

/* Without an image the block is a plain container and the prose takes the
   column; the grid only appears once there is a second column. The picture is
   an accent rather than a second half — a narrow fixed column, so the block
   keeps roughly the height it had when the text ran alone. */
.join--with-media {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(180px, 20vw, 260px);
  gap: clamp(24px, 3vw, 44px);
  /* The picture sits level with the middle of the text rather than riding up
     against its first line, so short prose does not leave it stranded high. */
  align-items: center;
}
.join__media { margin: 0; }
/* A fixed ratio so the column height does not swing with whatever image is
   chosen in the admin, and a gold rule under it to echo the panel's own. */
.join__img {
  display: block; width: 100%;
  aspect-ratio: 4 / 3; object-fit: cover;
  border-bottom: var(--rule-gold);
}

@media (max-width: 720px) {
  .join--with-media { grid-template-columns: 1fr; }
  /* Stacked, the picture reads as a footnote to the text rather than a
     second column, so it follows the text, stays small and sits centred. */
  .join__media { order: 2; max-width: 260px; margin-inline: auto; }
}

/* --- join us page ------------------------------------------------------ */

/* Openings read as a short list, not as a column of prose, so text alone
   would leave a dead column on the right of the panel. The topic list fills
   it: text left, topics right, stacking back into one column once there is no
   longer room for both. */
.join-openings--split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}
@media (max-width: 900px) {
  .join-openings--split { grid-template-columns: 1fr; }
}

.join-topics__heading {
  font-size: 1rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text); margin: 0;
}
.join-topics { list-style: none; margin: 16px 0 0; padding: 0; }
/* Side by side the list is one column; stacked it has the full panel to spend,
   so it splits in two rather than running as one long ragged strip. */
@media (min-width: 901px) {
  .join-openings--split .join-topics__heading { margin-top: .3em; }
}
@media (max-width: 900px) {
  /* Stacked, the grid gap above already separates the list from the prose. */
  .join-topics {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 40px;
  }
}
@media (max-width: 640px) { .join-topics { grid-template-columns: 1fr; } }
.join-topic {
  border-top: 1px solid var(--rule);
  padding: 12px 0 12px 18px;
  position: relative;
}
/* Gold tick in place of a bullet, echoing the rules elsewhere on the site.
   The pseudo spans the title's first line box, starting at the padding edge,
   and the bar is centred inside it, so it sits on the text's midline instead
   of at a fixed offset that drifts when the type or the padding changes. */
.join-topic::before {
  content: ""; position: absolute; left: 0; top: 12px;
  width: 8px;
  height: 1.6em; /* fallback for browsers without lh units */
  height: 1lh;
  background: linear-gradient(var(--ksu-gold) 0 0) left center / 8px 2px no-repeat;
}
.join-topic__title { display: block; font-weight: 600; }
.join-topic__desc {
  display: block; margin-top: 3px;
  font-size: .9rem; color: var(--text-muted);
}

/* Two plain sections abreast. auto-fit rather than a fixed pair so the row
   collapses to one column on a narrow window — and so a single section with
   no partner still fills the width instead of sitting in half of it. */
.join-duo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}
/* The contact details drawn as the head of a message. Paper-white inside the
   grey panel so it reads as a thing shown rather than another paragraph, and
   square-cornered like everything else here — the gold rule on top is the same
   one the panels and headings close with. */
.email-card {
  margin-top: 26px;
  max-width: 34rem;
  background: var(--bg);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--ksu-gold);
}
.email-card__bar {
  margin: 0;
  padding: 10px 18px;
  border-bottom: 1px solid var(--rule);
  font-size: .68rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-muted);
}
.email-card__fields { margin: 0; padding: 4px 18px; }
/* Label column wide enough for "Subject"; the value wraps under itself rather
   than under the label once the column gets tight. */
.email-card__row {
  display: grid; grid-template-columns: 72px minmax(0, 1fr);
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--rule);
}
.email-card__row:last-child { border-bottom: 0; }
.email-card__row dt {
  font-size: .7rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-faint);
  padding-top: .22em;
}
.email-card__row dd {
  margin: 0; font-size: .95rem; line-height: 1.45;
  overflow-wrap: anywhere;
}
.email-card__attach {
  display: flex; align-items: baseline; gap: 8px;
  margin: 0; padding: 11px 18px;
  border-top: 1px solid var(--rule);
  background: var(--bg-subtle);
  font-size: .82rem; color: var(--text-muted);
}
.email-card__clip { flex: none; transform: translateY(2px) rotate(-45deg); }
@media (max-width: 420px) {
  /* Two clauses of attachment note is one line too many on a phone. */
  .email-card__attach-extra { display: none; }
}

.join-footnote { margin-top: 28px; }
.join-cta { margin: 24px 0 0; }
.join-cta__hint { margin: 10px 0 0; }

/* --- live filters: hide the submit button when JS drives the form ------ */

.filters--live .filters__submit { display: none; }

/* Screen-reader-only text (used for the "opens in a new tab" note). */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip: rect(0 0 0 0); clip-path: inset(50%);
}

/* --- KSU lockup in the header ------------------------------------------ */

.site-header__identity {
  display: flex; align-items: center; gap: 18px;
  margin-right: auto; min-width: 0;
}
.site-header__ksu {
  display: flex; align-items: center;
  background-image: none; flex: 0 0 auto;
  padding-right: 18px; border-right: 1px solid var(--rule);
}
/* Both official files share a viewBox, so one height serves both.
   The lockup follows the header's own ground, which is reversed against the
   page: the white file goes on the black masthead of the light theme, the
   standard file on the white masthead of the dark one. */
.ksu-logo { display: block; width: auto; height: 38px; }
.ksu-logo--light { display: none; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ksu-logo--light { display: block; }
  :root:not([data-theme="light"]) .ksu-logo--dark { display: none; }
}
:root[data-theme="dark"] .ksu-logo--light { display: block; }
:root[data-theme="dark"] .ksu-logo--dark { display: none; }

/* The gutter takes up to 144px of the line, so the header has to start
   tightening well before the usual tablet breakpoint — otherwise the
   theme toggle wraps onto a second row. */
@media (max-width: 1100px) {
  .site-header__inner { gap: 18px; }
  .site-header__identity { gap: 14px; }
  .site-header__brand { font-size: 1.15rem; }
  .site-header__ksu { padding-right: 12px; }
  .ksu-logo { height: 30px; }
}
/* The full lab name fits on one line only once the column is near its full
   1080px. Narrower than that, the name breaks onto two balanced lines so the
   header stays a single row. A zero basis makes the identity take only what
   the nav and toggle leave; with `auto`, the name's one-line width would
   decide the line break and push the toggle onto a row of its own. */
@media (max-width: 1220px) {
  .site-header__identity { flex: 1 1 0; }
  .site-header__brand { line-height: 1.15; text-wrap: balance; }
}
/* Below this the four items no longer fit on one line, so the nav is given
   a row of its own. `order` keeps the toggle up on the brand row rather
   than letting it fall past the full-width nav onto a third row. */
@media (max-width: 960px) {
  /* Asymmetric: the stacked header loses the 72px min-height that used to
     centre it, so the top needs padding of its own or the logo sits flush
     against the top of the viewport. */
  .site-header__inner { gap: 8px 16px; padding-block: 20px 14px; min-height: 0; }
  /* A basis narrower than the brand's max-content width. Flex breaks lines
     from the basis, before shrinking, so `auto` here would let the long lab
     name claim the whole row and push the toggle onto one of its own. */
  .site-header__identity { flex: 1 1 12rem; gap: 12px; }
  .theme-toggle { order: 1; }
  .site-nav { order: 2; flex: 1 1 100%; margin-right: auto; }
}

@media (max-width: 640px) {
  .site-header__brand { font-size: 1.05rem; }
  .site-nav { gap: 18px; }
  .site-nav a { font-size: .74rem; letter-spacing: .06em; }
}

/* --- small-screen menu --------------------------------------------------

   With JS the stacked nav row above becomes a "Quick links" dropdown, so the
   header is one row again. Everything here is gated on `.js` (set pre-paint
   in <head>): without it the button stays hidden and the stacked nav is the
   fallback. The panel is a square box hung under the button's side of the
   header, positioned against the sticky header so it overlays the page
   rather than pushing it down. */

/* Styled as one more nav link with a chevron, not as a box: the label in
   --menu-ink, and the nav's gold underline on hover and while open. */
.nav-toggle {
  display: none;
  flex: 0 0 auto;
  gap: 8px; align-items: center;
  padding: 6px 0; cursor: pointer;
  font: inherit; font-size: .78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; white-space: nowrap;
  background: transparent; color: var(--menu-ink);
  border: 0; border-bottom: 2px solid transparent; border-radius: 0;
  transition: border-color .15s;
}
/* Mouse only: a tap leaves :hover stuck on touch screens, which would leave
   a grey line under the label after the menu is closed. */
@media (hover: hover) {
  .nav-toggle:hover { border-bottom-color: var(--rule); }
}
.nav-toggle[aria-expanded="true"] { border-bottom-color: var(--ksu-gold); }
.nav-toggle:focus-visible { outline: 2px solid var(--ksu-gold); outline-offset: 4px; }

/* A chevron from two borders; it flips while open. */
.nav-toggle__icon {
  width: 6px; height: 6px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .2s ease;
}
.nav-toggle[aria-expanded="true"] .nav-toggle__icon {
  transform: translateY(2px) rotate(-135deg);
}

@media (max-width: 960px) {
  /* One row, always: the name wraps inside its share rather than letting the
     menu or the toggle drop onto a line of their own. */
  .js .site-header__inner { padding-block: 14px; flex-wrap: nowrap; }
  /* Extra room so the toggle doesn't read as part of the menu button. */
  .js .theme-toggle { order: 0; margin-left: 8px; }
  .js .nav-toggle { display: inline-flex; }

  /* Right-aligned with the header's content edge, just below the gold rule
     (`top` is measured from the padding box). */
  .js .site-nav {
    display: none;
    position: absolute; right: var(--gutter); top: calc(100% + 5px);
    width: min(260px, calc(100% - var(--gutter) * 2));
    flex-direction: column; gap: 0;
    padding: 4px 0;
    background: var(--bg);
    border: 1px solid var(--rule);
    border-bottom: 3px solid var(--ksu-gold);
    box-shadow: 0 14px 28px -10px rgba(0, 0, 0, .4);
  }
  .js .site-header.is-menu-open .site-nav { display: flex; }

  .js .site-nav a {
    font-size: .8rem; letter-spacing: .08em;
    padding: 12px 16px;
    color: var(--menu-ink);
    border-bottom: 1px solid var(--rule);
  }
  .js .site-nav a:last-child { border-bottom: 0; }
  .js .site-nav a:hover {
    color: var(--menu-ink); background: var(--bg-subtle); border-bottom-color: var(--rule);
  }
  .js .site-nav a:last-child:hover { border-bottom: 0; }
  /* The gold underline would clash with the row dividers; mark the current
     page with a bar at the start of the row instead. */
  .js .site-nav a[aria-current="page"] {
    color: var(--menu-ink); border-bottom-color: var(--rule);
    box-shadow: inset 3px 0 0 var(--ksu-gold);
  }
  .js .site-nav a[aria-current="page"]:last-child { border-bottom: 0; }
}

/* The lockup stays on phones rather than being dropped: it is the university
   mark, and the footer keeps it anyway. It shrinks instead, and the divider
   loses most of its padding. */
@media (max-width: 520px) {
  .site-header__ksu { padding-right: 8px; }
  .ksu-logo { height: 24px; }
  .site-header__identity { gap: 8px; }
  .site-header__brand { font-size: 1rem; }
  /* Lockup, name, menu and toggle still share one row: the label drops to
     "Links", and the gaps and the toggle give up a few pixels each. */
  .js .site-header__inner { gap: 10px; }
  .js .nav-toggle__quick { display: none; }
  .js .theme-toggle { width: 30px; height: 30px; }
}

/* --- person contact details -------------------------------------------- */

.person-contact { margin: 20px 0 24px; }
.person-contact__row { display: flex; gap: 16px; margin-bottom: 10px; }
.person-contact dt {
  flex: 0 0 66px;
  font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-faint); padding-top: 3px;
}
.person-contact dd { margin: 0; font-size: .92rem; line-height: 1.5; }

/* --- profile link icons ------------------------------------------------- */

.link-icon {
  width: 15px; height: 15px; flex: 0 0 auto;
  display: block; /* kills the inline-baseline gap */
}
.chip--link { gap: 8px; padding: 6px 13px; }
.chip--link:hover .link-icon { color: var(--accent-line); }
