/* Self-hosted — was `@import url(api.fontshare.com/...)`. Same family, same
   weights actually used on the site (300/400/500/700; nothing uses 900), just
   served from fonts/satoshi/ instead of a third-party CDN: one less external
   dependency, works if that CDN is ever slow/down/blocked, and loads from the
   same origin as everything else. */
@font-face {
  font-family: 'Satoshi';
  src: url('fonts/satoshi/Satoshi-Light.woff2') format('woff2'),
       url('fonts/satoshi/Satoshi-Light.woff') format('woff');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Satoshi';
  src: url('fonts/satoshi/Satoshi-Regular.woff2') format('woff2'),
       url('fonts/satoshi/Satoshi-Regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Satoshi';
  src: url('fonts/satoshi/Satoshi-Medium.woff2') format('woff2'),
       url('fonts/satoshi/Satoshi-Medium.woff') format('woff');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Satoshi';
  src: url('fonts/satoshi/Satoshi-Bold.woff2') format('woff2'),
       url('fonts/satoshi/Satoshi-Bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

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

:root {
  --black:   #0a0a0a;
  --white:   #fff;
  --line:    #c8c8c8;
  --line-mid:#d8d8d8;
  --muted:   #555;
  --dim:     #777;
  --faint:   #999;
  --secondary: #222;
  --bg-hover:  #f5f5f5;

  /* Specialisation colours — matched to letter PNG colours */
  --col-research:     #c8e800;  /* lime    — M */
  --col-animation:    #ff20c0;  /* magenta — A */
  --col-games:        #1eceff;  /* cyan    — G */
  --col-interactivity:#ff6b1a;  /* orange  — I */

  /* Tint backgrounds */
  --tint-research:     #f7fcd0;
  --tint-animation:    #ffe8f9;
  --tint-games:        #d6f8ff;
  --tint-interactivity:#fff0e6;
}

html, body {
  background: var(--white);
  color: var(--black);
  font-family: 'Satoshi', sans-serif;
  font-size: 15px;
  font-weight: 400;
  line-height: 1.6;
  min-height: 100vh;
}

/* ── PAGE TRANSITIONS ────────────────────────────── */

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.4s;
  animation-timing-function: ease;
}

/* Keep the header constant across page navigations (not part of the fade) */
.nav { view-transition-name: site-header; }

/* Universal fallback — subtle fade-in of page content on load (nav excluded) */
@keyframes pageFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* fade page content, but never elements that manage their own visibility
   (e.g. fixed modal/dialog overlays that default to opacity:0).
   `.no-page-fade` is the explicit opt-out for shared widgets; `.modal-overlay`
   is kept because per-page modals already use that name for their backdrops. */
body > :not(.nav):not(.modal-overlay):not(.no-page-fade) {
  animation: pageFadeIn 0.4s ease both;
}

@media (prefers-reduced-motion: reduce) {
  body > :not(.nav):not(.modal-overlay):not(.no-page-fade) { animation: none; }
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* ── NAV ─────────────────────────────────────────── */

.nav {
  display: flex;
  align-items: center;
  height: 56px;
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  background: var(--white);
  z-index: 100;
}

/* green header line — doubles as a page-load progress bar */
.nav::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -1px;
  width: 100%;
  height: 2px;
  background: #c8e800;
  transform-origin: left center;
  animation: navLoadBar 0.9s cubic-bezier(0.4, 0, 0.2, 1) both;
  z-index: 101;
}

@keyframes navLoadBar {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .nav::after { animation: none; }
}

.nav-wordmark {
  padding: 12px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  border-right: 1px solid var(--line);
  cursor: pointer;
  user-select: none;
  transition: opacity 0.15s ease;
}

.nav-wordmark:hover { opacity: 0.6; }

.nav-wordmark-magi {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--black);
}

.nav-wordmark-icon {
  height: 32px;
  width: auto;
  display: block;
}

.nav-wordmark-sub {
  font-size: 11px;
  font-weight: 300;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.nav-links { display: flex; height: 100%; }

.nav-link {
  padding: 0 18px;
  font-size: 11px;
  font-family: 'Satoshi', sans-serif;
  font-weight: 300;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  height: 100%;
  display: flex;
  align-items: center;
  border-right: 1px solid var(--line);
  cursor: pointer;
  transition: color 0.1s;
  white-space: nowrap;
  user-select: none;
  text-decoration: none;
}

.nav-link:hover { color: var(--black); }

.nav-link.active {
  color: var(--white);
  background: var(--black);
}

.nav-live   { display: flex; align-items: center; gap: 7px; }
.nav-dot    { width: 5px; height: 5px; border-radius: 50%; background: var(--black); animation: blink 1.6s ease-in-out infinite; flex-shrink: 0; }

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

.nav-right  { margin-left: auto; display: flex; height: 100%; }
.nav-right .nav-link { border-right: none; border-left: 1px solid var(--line); }

/* ── SEARCH (expands leftward, pushing the earlier nav-right buttons along
   with it since this sits last and nav-right is pinned to the nav's right
   edge — no JS positioning needed, plain layout does the "sliding"). ────── */

.nav-search {
  position: relative;
  display: flex;
  align-items: center;
  height: 100%;
  border-left: 1px solid var(--line);
}

.nav-search-input {
  width: 0;
  min-width: 0;
  height: 100%;
  padding: 0;
  border: none;
  outline: none;
  background: none;
  font-family: 'Satoshi', sans-serif;
  font-size: 12px;
  color: var(--black);
  transition: width 0.28s cubic-bezier(0.4, 0, 0.2, 1), padding 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav-search-input::placeholder { color: var(--faint); }
.nav-search.open .nav-search-input { width: 200px; padding: 0 6px 0 16px; }

.nav-search-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 0 18px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--muted);
  transition: color 0.1s;
  flex-shrink: 0;
}
.nav-search-btn:hover { color: var(--black); }
.nav-search.open .nav-search-btn { color: var(--black); }

.nav-search-label-wrap { position: relative; display: inline-flex; align-items: center; justify-content: center; }

.nav-search-label {
  font-family: 'Satoshi', sans-serif;
  font-size: 11px;
  font-weight: 300;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: opacity 0.15s ease;
}

.nav-search-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.nav-search.open .nav-search-label { opacity: 0; }
.nav-search.open .nav-search-icon { opacity: 1; }

/* ── EXPO DROPDOWN (previous years) ───────────────── */

.nav-expo { position: relative; display: flex; align-items: stretch; height: 100%; }
.nav-expo-link { border-right: none; }

/* The vertical divider that normally sits at EXPO's right edge slides right
   to meet the dropdown's right border as it opens — same line, it just
   travels there.
   `right: calc(100% - 240px)` lands it exactly on the menu's right edge
   (menu is left:0, width:240px) regardless of how wide "EXPO ↗" renders. */
.nav-expo-accent {
  position: absolute;
  top: 0;
  right: 0;
  width: 1px;
  height: 100%;
  background: var(--line);
  transition: right 0.25s ease, background 0.15s ease;
  z-index: 201;
  pointer-events: none;
}
.nav-expo:hover .nav-expo-accent,
.nav-expo.open .nav-expo-accent {
  right: calc(100% - 240px);
  background: var(--black);
}

.nav-expo-menu {
  position: absolute;
  top: 100%;
  left: 0;
  width: 240px;
  background: var(--white);
  border: 1px solid var(--line);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.1);
  opacity: 0;
  pointer-events: none;
  transform: translateY(-4px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 200;
}
.nav-expo:hover .nav-expo-menu,
.nav-expo.open .nav-expo-menu { opacity: 1; pointer-events: auto; transform: translateY(0); }

.nav-expo-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 12px 16px;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  transition: background 0.1s;
}
.nav-expo-item:last-child { border-bottom: none; }
.nav-expo-item:hover { background: var(--bg-hover); }
.nav-expo-year { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; color: var(--black); flex-shrink: 0; width: 30px; }
.nav-expo-name { font-size: 12px; color: var(--secondary); flex: 1; }

/* ── HERO ────────────────────────────────────────── */

.hero {
  position: relative;
  height: 88vh;
  min-height: 560px;
  max-height: 820px;
  overflow: hidden;
}

.hero-rule {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 2px;
  background: #c8e800;
  z-index: 5;
}

.hero-logo {
  position: absolute;
  top: 42%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
}

.hero-tagline-wrap {
  position: absolute;
  bottom: 96px;
  left: 50%;
  transform: translateX(-50%);
  text-align: center;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.hero-title-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.hero-tagline-wrap .hero-badge {
  display: inline-block;
  color: var(--black);
  font-family: "museo", "Satoshi", sans-serif;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.hero-tagline-wrap .hero-rmit {
  height: 50px;
  width: auto;
  display: block;
  opacity: 0.5;
}

.hero-tagline-wrap .strip-tagline {
  font-size: 13px;
  font-weight: 400;
  color: var(--secondary);
  line-height: 1.5;
  white-space: nowrap;
}

@media (max-width: 640px) {
  .hero-tagline-wrap .hero-badge {
    font-size: 13px;
    letter-spacing: 0.08em;
    white-space: normal;
  }
  .hero-tagline-wrap .strip-tagline { white-space: normal; }
}

/* ── UPLOAD CTA ──────────────────────────────────── */

.upload-cta {
  border-bottom: 1px solid var(--line);
  background: #f7fcd0;
}

.upload-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 40px 32px;
  gap: 20px;
}

.upload-title {
  font-size: 18px;
  font-weight: 500;
  color: var(--black);
  margin-bottom: 6px;
}

.upload-desc {
  font-size: 12px;
  color: #555;
  line-height: 1.7;
  max-width: 440px;
}

.upload-btn {
  font-family: 'Satoshi', sans-serif;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--white);
  background: var(--black);
  border: 1px solid var(--black);
  padding: 14px 28px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.1s;
  flex-shrink: 0;
}

.upload-btn:hover { background: #333; }

/* ── NEWS & EVENTS ───────────────────────────────── */

#news {
  background: #0a0a0a;
}

#news .bar {
  background: #0a0a0a;
  border-bottom-color: #e0e0e0;
  border-top-color: #e0e0e0;
}

#news .bar-title { color: #666; }
#news .bar-link  { color: #555; border-left-color: #e0e0e0; }
#news .bar-link:hover { color: #fff; }

.news-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  border-bottom: 1px solid #e0e0e0;
  /* Floor so the row can't shrink indefinitely as the window narrows. Meta,
     gaps and padding are fixed px amounts that DON'T shrink along with the
     aspect-ratio-driven row height below — so past a certain width, the
     leftover for .news-desc (thumb 65% + meta + gaps subtracted from a
     shrinking row) runs out and hits 0 before the row itself is actually
     that short, and text starts overlapping neighbouring elements instead of
     just clipping cleanly. Below this floor the row simply stops tracking
     40/9 exactly — the featured video crops slightly via its existing
     object-fit:cover rather than fitting edge-to-edge — which is the
     graceful trade-off, over letting text collide. Reset to none on the
     mobile scroller (see that breakpoint), where cards are auto-height and
     this isn't the mechanism keeping things apart. */
  min-height: 320px;
  /* Sized so the featured (2fr-of-5) column comes out at exactly 16:9 — the
     trailer's native ratio — with no cropping or letterboxing needed.
     width:height of that column at 16:9 means grid width:height = 40:9
     (since the column is 2/5 of the grid width: (2/5)*(9/16) = 9/40, and the
     ratio is the inverse of that fraction). All four cards stretch to this
     row height by default (align-items: stretch); the other three fix
     .news-thumb at exactly half of it and let .news-desc's flex:1 absorb the
     rest above .news-meta (see those rules) — same "fill whatever's left"
     mechanism the featured card's body uses on its own, which is why "Read
     more" lands at the same height on every card. This stays pinned
     even while Call for Works'
     "Read more" is open — its extra content is an absolute overlay
     (.news-more-wrap), not part of the card's flex flow, so it can never
     push this row taller. Reset to auto on the mobile scroller, where the
     row is a single card's natural height instead of four sharing one row. */
  aspect-ratio: 40 / 9;
}

.news-card {
  /* Padding lives in vars so .news-thumb can bleed to the card edges with a
     matching negative margin at every breakpoint (see .news-thumb). */
  --news-pad-x: 24px;
  --news-pad-y: 24px;
  position: relative;   /* anchors .news-more-wrap on the featured card */
  border-right: 1px solid #e0e0e0;
  padding: var(--news-pad-y) var(--news-pad-x);
  display: flex;
  flex-direction: column;
  gap: 8px;
  cursor: pointer;
  background: #0a0a0a;
  transition: background 0.1s;
  /* The grid row height is fixed (see .news-grid, sized to the featured
     video's own aspect ratio) rather than growing to fit content, so these
     four cards must fit inside it instead of the row growing to fit them.
     overflow:hidden is the backstop if desc/meta ever overrun. min-height:0
     lets a flex child shrink below its content size at all — without it
     flexbox refuses to compress a column past that. */
  overflow: hidden;
  min-height: 0;
}

.news-card:hover { background: #141414; }
.news-card-featured { border-right: 1px solid #e0e0e0; }
/* The last card's own divider used to blend into the section's own edge
   border; now that the section has no side border, this would otherwise
   read as a stray line at the right edge of the screen. */
.news-card:last-child { border-right: none; }

/* ── THUMBNAIL: 3:2 landscape crop, tag top-left, title bottom-left ──
   Locked to a thumbnail ratio instead of stretching to fill the card —
   the shared row height is set by the featured 16:9 trailer, so without
   this the three 1fr columns end up near-square. 3:2 is taller than 16:9
   but still rectangular; flex-shrink lets it compress on short rows so
   desc + meta are never clipped. Bleeds to the card's top/side edges via
   negative margin = the card's own padding. */
.news-thumb {
  position: relative;
  /* Fixed at exactly half the card's content-box height (i.e. half the row
     height, since all four cards stretch to the same row) — flex-basis:50%
     resolves against that content box regardless of what .news-desc needs.
     .news-desc is the flexible one now (flex:1, see below): it absorbs
     whatever's left after this fixed 50% and .news-meta's own height, which
     is also why meta's position is untouched by this change — the same
     "whatever's left above meta always gets fully absorbed" guarantee, just
     with desc alone doing the absorbing instead of splitting it with thumb.
     A flex-basis PERCENTAGE, not a fixed aspect-ratio, is what makes this
     scale correctly with viewport width without reintroducing the earlier
     bug (aspect-ratio sizing off the thumb's own width independent of the
     row's real height, which left dead space that varied per card). */
  flex: 0 0 65%;
  min-height: 0;
  overflow: hidden;
  margin: calc(-1 * var(--news-pad-y)) calc(-1 * var(--news-pad-x)) 0;
}

.news-thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* portrait image rotated to landscape */
.news-thumb-rotated img {
  width: 170%;
  height: 170%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-90deg);
}

.news-thumb-tag {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 1;
  background: #000;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 4px 9px;
}

.news-thumb-titlewrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: 10px 12px;
}

.news-thumb-title {
  display: block;
  font-size: 14px;
  padding-left: 15px;
  font-weight: 700;
  line-height: 1.25;
  color: #fff;
  /* Soft shadow keeps the title readable on light photos now that the
     old bottom fade-gradient is gone. */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85), 0 0 10px rgba(0, 0, 0, 0.45);
}

/* Summary sits directly under the thumb (no margin-top:auto — that used
   to pin it to the card bottom and leave a gap). line-clamp allows ~3
   lines so the copy can run a sentence longer than a one-liner. */
.news-desc {
  /* Bumped from 12px/1.5 now that shrinking the thumb's share (see
     .news-thumb) freed up real vertical room for this. */
  font-size: 14px;
  color: #aaa;
  line-height: 1.6;
  /* Extra breathing room above this specifically (the card's own `gap`
     already puts 8px between every child — this adds to that, just between
     thumb and desc, without touching the desc→meta spacing). */
  margin-top: 14px;
  /* The sole flexible filler now: .news-thumb is fixed at 50%, so this grows
     to take up everything else that's left above .news-meta. */
  flex: 1;
  min-height: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
}

.news-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-top: 1px solid #e0e0e0;
  padding-top: 10px;
  flex-shrink: 0;   /* date + link must stay visible even if the card is tight */
  /* The real bottom-anchor: .news-desc (flex:1, .news-thumb is now a fixed
     50%) — or, on the featured card, .news-body — always grows to absorb
     ALL leftover height, so meta lands at the same height on every card.
     margin-top:auto here is just a defensive backstop (a no-op as long as
     that flex:1 is in place and correctly absorbing the leftover space). */
  margin-top: auto;
}

.news-date { font-size: 11px; letter-spacing: 0.08em; color: #999; }

.news-link {
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
  color: #fff; text-decoration: none; transition: opacity 0.1s;
}

.news-link:hover { opacity: 0.6; }

/* Swipe-hint dots. Hidden by default — the desktop grid shows all four cards
   at once, so there's nothing to page through. Turned on in the mobile
   scroller media query (see below), where the cards become a swipeable row. */
.news-dots { display: none; }

/* ── FEATURED EXHIBITION CARD — full-bleed low-opacity video ──
   Unlike the other three cards (image thumbnail with the tag/title pinned as
   overlays), this one keeps the older treatment: the trailer fills the whole
   card, dimmed, with the label/title/copy laid over it as plain text. The
   card is 16:9 (the grid's 40/9 ratio sizes this 2fr column to exactly that),
   so the 16:9 clip fills it with no cropping. */
.news-bg-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: var(--news-video-opacity, 0.3);
  z-index: 0;
  pointer-events: none;
}
/* Lift every card child above the video — except the reveal overlay, which
   manages its own stacking (position:absolute, z-index:2). */
.news-card-featured > :not(.news-bg-video):not(.news-more-wrap) {
  position: relative;
  z-index: 1;
}
/* Two-line title, vertically centred over the video — takes over .news-body's
   old job as the flex:1 filler between the top of the card and .news-meta, so
   removing the tag/description doesn't disturb where "Read more" sits. */
.news-title-stack {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;   /* centres both lines horizontally within the card */
  text-align: center;
  gap: 8px;
}
.news-title-main {
  /* Explicit family + 700, matching the rest of the site's UI type. 700 (not
     800) because Satoshi only ships weights 300/400/500/700 (see the
     @font-face block near the top of this file) — 800 has no matching face,
     so the browser would fake a synthetic bold instead of rendering the
     real one. */
  font-family: 'Satoshi', sans-serif;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.15;
  color: #fff;
}
.news-title-sub {
  font-family: 'Satoshi', sans-serif;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.04em;
 /* text-transform: uppercase; */
  color: #fff;
}

/* ── CALL FOR WORKS — overlay reveal ───────────────
   Extra description + the Teams submission link, opened by "Read more".
   position:absolute + inset:0 takes this OUT of the card's flex layout
   entirely — its content height has no way to affect the card's size or the
   grid row it shares with the other three cards, which is the actual fix for
   the earlier bug where opening it grew the whole row. It sits directly on
   the card (.news-card has position:relative), above the thumbnail, and
   fades/scales in rather than pushing anything. pointer-events:none while
   closed so it can't intercept clicks meant for the thumb behind it. */
.news-more-wrap {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  background: rgba(6, 6, 6, 0.96);
  opacity: 0;
  transform: scale(0.98);
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.news-more-wrap.is-open {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

.news-more-inner {
  position: relative;
  width: 100%;
  max-height: 100%;
  overflow-y: auto;    /* safety valve if a narrow/short viewport can't fit it */
  padding: 30px 40px;
}

.news-more-close {
  position: absolute;
  top: -4px;
  right: -4px;
  background: none;
  border: none;
  font-size: 22px;
  line-height: 1;
  color: #999;
  cursor: pointer;
  padding: 6px;
}
.news-more-close:hover { color: #fff; }

.news-more-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #999;
  margin-bottom: 10px;
}
.news-more-title {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: #fff;
  margin-bottom: 14px;
}

.news-more-text {
  font-size: 13px;
  line-height: 1.75;
  color: #bbb;
  margin-bottom: 14px;
  max-width: 52ch;
}
.news-more-text:last-of-type { margin-bottom: 20px; }

.news-submit-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #111;
  background: #fff;
  text-decoration: none;
  padding: 12px 22px;
  transition: background 0.14s, color 0.14s;
}
.news-submit-btn:hover { background: #5b5fc7; color: #fff; }   /* Teams purple */

@media (max-width: 760px) {
  .news-more-inner { padding: 26px 22px; }
  .news-more-title { font-size: 17px; }
}

/* ── MEDIA GALLERY ───────────────────────────────── */

/* Masonry via CSS multi-column. The previous version placed items into fixed
   grid rows (210px / 160px / 420px) and used object-fit:cover to fill them,
   which is what cropped the images. Here each item keeps its own natural
   height instead, so nothing is ever cut off — the column flow does the
   arranging, and the varying image heights are what stagger the columns.
   Reading order runs down each column rather than across; that's fine for a
   gallery whose contents are shuffled anyway. */
.gallery-grid {
  columns: 3;
  column-gap: 34px;
  border-bottom: 1px solid var(--line);
  padding: 44px 48px 20px;
  background: var(--white);
}

/* Each tile is an <a> to the work's portfolio (opens in a new tab), so the
   inherited link styling is reset back to the page's own type colours. */
.gallery-item {
  /* Keeps an item's picture and its caption together — without this, a column
     break can land between the image and the title that belongs to it. */
  break-inside: avoid;
  page-break-inside: avoid;    /* older Firefox */
  display: inline-block;       /* stops margins collapsing across columns */
  vertical-align: top;         /* no baseline gap under each tile */
  width: 100%;
  margin-bottom: 46px;
  text-decoration: none;
  color: inherit;
}
/* Only linked tiles get the pointer — a work with no portfolio on file
   renders as an href-less anchor, which shouldn't look clickable. */
.gallery-item[href] { cursor: pointer; }

/* The image's own box: clips the hover zoom without cropping the image
   itself, since the box takes its height from the picture. */
.gallery-item-media {
  overflow: hidden;
  background: var(--bg-hover);
}

.gallery-item img {
  width: 100%;
  height: auto;                /* natural aspect ratio — never cropped */
  display: block;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.8s ease;
}

/* Hover response only where there's actually somewhere to go. */
.gallery-item[href]:hover img,
.gallery-item[href]:focus-visible img { transform: scale(1.035); }

/* These are links now, so they need a visible keyboard focus ring. */
.gallery-item[href]:focus-visible {
  outline: 2px solid var(--black);
  outline-offset: 4px;
}

/* ── Caption, below the image (not an overlay) ── */

/* Each tile carries its specialisation's colour as --chip-* , set from the
   data-spec attribute JS writes on it. Same four colours and the same ink
   pairings used by the specialisation panels, staff tags and Selected Works
   badges, so a category reads identically wherever it appears on the site.
   The fallback keeps an unrecognised label legible rather than invisible. */
.gallery-item[data-spec="research"]      { --chip-bg: var(--col-research);      --chip-ink: #4a5200; }
.gallery-item[data-spec="animation"]     { --chip-bg: var(--col-animation);     --chip-ink: #fff;    }
.gallery-item[data-spec="games"]         { --chip-bg: var(--col-games);         --chip-ink: #003a4a; }
.gallery-item[data-spec="interactivity"] { --chip-bg: var(--col-interactivity); --chip-ink: #fff;    }

.gallery-item-label {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 4px 9px 5px;
  margin-top: 14px;
  background: var(--chip-bg, var(--bg-hover));
  color: var(--chip-ink, var(--dim));
}

.gallery-item-title {
  font-size: 21px;
  font-weight: 500;
  letter-spacing: -0.015em;
  line-height: 1.2;
  color: var(--black);
  margin-top: 5px;
  transition: opacity 0.2s ease;
}

.gallery-item[href]:hover .gallery-item-title,
.gallery-item[href]:focus-visible .gallery-item-title { opacity: 0.55; }

.gallery-item-student {
  font-size: 11px;
  color: var(--muted);
  margin-top: 6px;
}

/* ── STAT ──────────────────────────────────────────── */

.stat {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-mid);
}

.stat:first-child { border-top: 1px solid var(--line-mid); }
.stat-n     { font-size: 18px; font-weight: 500; color: var(--black); line-height: 1; }
.stat-label { font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim); }

/* ── LOGO ─────────────────────────────────────────── */

.logo-wrap {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(8px, 2vw, 24px);
}

.letter-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 0 0 auto;
}

.letter-label {
  position: absolute;
  bottom: -2px;
  left: 50%;
  transform: translateX(-50%) translateY(100%);
  background: var(--label-color);
  color: var(--label-text);
  font-family: 'Satoshi', sans-serif;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  padding: 5px 10px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 10;
}

.letter-wrap:hover .letter-label {
  opacity: 1;
  transform: translateX(-50%) translateY(calc(100% + 8px));
}

/* ── INTERACTION HINTS ("click me" / "drag me") ──── */

.logo-hint {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  background: var(--black);
  color: var(--white);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  padding: 6px 11px;
  border-radius: 3px;
  opacity: 0;
  pointer-events: none;
  z-index: 40;
  transform: translateX(-50%) translateY(6px) scale(0.94);
  /* Quick pop rather than the old 0.3s drift — now that it's triggered by
     hover instead of an ambient timer, it needs to read as an immediate
     response to the cursor arriving, not something catching up a beat later. */
  transition: opacity 0.12s ease, transform 0.12s ease;
}

.logo-hint::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--black);
}

.logo-hint.show {
  opacity: 1;
  animation: hintBob 1.6s ease-in-out infinite;
}

@keyframes hintBob {
  0%, 100% { transform: translateX(-50%) translateY(0)    scale(1); }
  50%      { transform: translateX(-50%) translateY(-5px) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .logo-hint.show { animation: none; transform: translateX(-50%) translateY(0) scale(1); }
}

.logo-letter {
  display: block;
  /* Sized by height, but the letters' aspect ratios sum to ~3.583, so the
     MAGI row width tracks this ×3.583 (+gaps). The old `min(280px, 23vw)`
     went FLAT at 280px above ~1217px viewport — so dragging a wide window
     smaller kept the logo pinned oversized until it suddenly started
     shrinking at 1217px. clamp() scales it continuously instead: it tracks
     18vw across the whole 830→1667px range (row stays ~70vw, ~15vw of air
     each side), and only pins at 300px on genuinely large monitors. Below
     ~760px the mobile block takes over with its own wider-spanning rule. */
  height: clamp(150px, 18vw, 300px);
  width: auto;
  flex: 0 0 auto;
  user-select: none;
  -webkit-user-drag: none;
  cursor: crosshair;
  filter: grayscale(1) brightness(0.13);
}

/* ── Per-letter hover animations ─────────────────── */

/* ── M — ORIGAMI PAPER FOLD-OUT ───────────────────── */

.m-stage {
  position: relative;
  display: inline-block;
  perspective: 1400px;
  perspective-origin: 50% 45%;
  cursor: pointer;
}

/* the real <img> IS the resting M (same shading as the other letters);
   the panel layer only appears while folding */
.m-stage #letter-m {
  display: block;
  opacity: 1;
  cursor: pointer;
  transition: filter 0.25s ease, opacity 0.25s ease,
              transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.m-stage:hover #letter-m {
  transform: translateY(-6px);
}

/* panel layer — hidden at rest, shown only during the fold */
.m-fold {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform-style: preserve-3d;
  pointer-events: none;
}

/* swap img → panels while folding */
#m-stage.folding .m-fold   { opacity: 1; }
#m-stage.folding #letter-m { opacity: 0; }

.m-panel {
  position: absolute;
  background-image: url('M-logo.png');
  background-repeat: no-repeat;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  filter: grayscale(1) brightness(0.13);
  transition: transform 0.34s cubic-bezier(0.45, 0.02, 0.2, 1),
              filter 0.25s ease,
              opacity 0.25s ease;
  will-change: transform;
}

/* soft crease shading that deepens mid-fold for a paper feel —
   masked to the logo silhouette so it follows the curves, not the tile box */
.m-panel::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
              rgba(0,0,0,0.40) 0%,
              rgba(0,0,0,0)    22%,
              rgba(0,0,0,0)    80%,
              rgba(255,255,255,0.16) 100%);
  -webkit-mask-image: url('M-logo.png');
          mask-image: url('M-logo.png');
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: var(--m-bg-w) var(--m-bg-h);
          mask-size: var(--m-bg-w) var(--m-bg-h);
  -webkit-mask-position: var(--m-bg-x) var(--m-bg-y);
          mask-position: var(--m-bg-x) var(--m-bg-y);
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}

/* When another letter is active, the M greys out like the rest of the wordmark. */
.logo-wrap[data-active] #letter-m,
.logo-wrap[data-active] .m-fold .m-panel {
  filter: grayscale(1) brightness(0.08);
  opacity: 0.3;
}
/* …but light it up full-colour when the M itself is the active spec */
.logo-wrap[data-active] #letter-m.is-active,
.logo-wrap[data-active] .m-stage:has(#letter-m.is-active) .m-fold .m-panel {
  filter: grayscale(0) brightness(1);
  opacity: 1;
}
/* hidden img must stay hidden mid-fold even if the M is the active spec */
#m-stage.folding #letter-m,
#m-stage.folding #letter-m.is-active { opacity: 0; }

/* crease shading lights up while a panel is mid-flip */
.m-panel.is-creasing::after {
  opacity: 1;
}

/* ── A — SHATTER / JENGA PLAYGROUND ──────────────── */

.a-shatter-layer {
  position: absolute;
  inset: 0;
  z-index: 4;
  overflow: hidden;
  pointer-events: auto;
}

.a-piece {
  position: absolute;
  top: 0;
  left: 0;
  background-repeat: no-repeat;
  cursor: grab;
  will-change: transform;
}

.a-piece:active { cursor: grabbing; }

.a-reset-hotspot {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(10, 10, 10, 0.28);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  pointer-events: auto;
  transition: color 0.15s ease, transform 0.15s ease;
}

.a-reset-hotspot:hover {
  color: rgba(10, 10, 10, 0.7);
  transform: scale(1.12);
}

#letter-a {
  transform-origin: center bottom;
  transition: filter 0.25s ease, opacity 0.25s ease, transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#letter-a:hover {
  transform: rotate(-6deg);
}

#letter-g {
  transition: filter 0.25s ease, opacity 0.25s ease, transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}
#letter-g:hover {
  transform: translateX(5px);
  cursor: grab;
}
#letter-g.is-dragging {
  cursor: grabbing;
  transition: filter 0.25s ease, opacity 0.25s ease;
}

/* ── TITTLE SCORE ─────────────────────────────────── */

.tittle-score {
  position: absolute;
  top: 0;
  left: calc(100% + 10px);
  font-size: 11px;
  font-weight: 500;
  font-family: 'Satoshi', sans-serif;
  letter-spacing: 0.08em;
  color: #ff6b1a;
  opacity: 0;
  transition: opacity 0.2s ease;
  white-space: nowrap;
  pointer-events: none;
}

.tittle-score.visible { opacity: 1; }
.tittle-score.lost    { color: var(--dim); }

.letter-i-container {
  position: relative;
  display: inline-block;
  cursor: crosshair;
}

/* white rectangle that covers the tittle area of the I */
.letter-i-decap {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 42%;
  background: var(--white);
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}

.letter-i-container.decapitated .letter-i-decap {
  opacity: 1;
}

/* bouncing tittle element */
.tittle-ball {
  position: fixed;
  pointer-events: auto;
  cursor: pointer;
  z-index: 9999;
  background: #ff6b1a;
  border-radius: 50%;
  width: min(122px, 5.0vw);
  height: min(122px, 5.0vw);
  display: none;
}

/* dim all when any spec active */
.logo-wrap[data-active] .logo-letter {
  filter: grayscale(1) brightness(0.08);
  opacity: 0.3;
}

/* hovered letter — full colour */
.logo-wrap[data-active] .logo-letter.is-active {
  filter: grayscale(0) brightness(1);
  opacity: 1;
}

/* ── SPECIALISATION PANELS ───────────────────────── */

.specs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-bottom: 1px solid var(--line);
  border-left: 1px solid var(--line);
}

.spec-panel {
  border-right: 1px solid var(--line);
  padding: 0;
  transition: background 0.3s ease;
  position: relative;
  overflow: hidden;
}

.spec-panel::before {
  content: '';
  display: block;
  height: 3px;
  width: 100%;
}

#spec-research::before      { background: var(--col-research); }
#spec-animation::before     { background: var(--col-animation); }
#spec-games::before         { background: var(--col-games); }
#spec-interactivity::before { background: var(--col-interactivity); }

/* ── SPEC HEADER ─────────────────────────────────── */

.spec-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  padding: 20px 20px 16px;
  border-bottom: 1px solid var(--line);
}

.spec-symbol {
  height: 80px;
  width: auto;
  object-fit: contain;
  display: block;
}

.spec-header-label {
  font-family: 'Satoshi', sans-serif;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 6px 12px 7px;
  align-self: flex-end;
  color: #111;
}

#spec-research      .spec-header-label { background: var(--col-research); }
#spec-animation     .spec-header-label { background: var(--col-animation); }
#spec-games         .spec-header-label { background: var(--col-games); }
#spec-interactivity .spec-header-label { background: var(--col-interactivity); }

#specialisations, #upload, #news, #archive, #gallery {
  margin-top: 0;
}

.specs, .students-grid, #view-grid {
  margin-bottom: 0;
}

/* section breathing room — top border on each bar already provides separation,
   add extra vertical space inside spec-inner and news cards */
.spec-inner { padding: 28px 24px 36px; }

.news-card { --news-pad-x: 28px; --news-pad-y: 28px; }

.spec-label {
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--dim);
  margin-bottom: 10px;
}

.spec-title {
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--black);
  margin-bottom: 12px;
  transition: color 0.25s ease;
}

.spec-desc {
  font-size: 13px;
  color: var(--secondary);
  line-height: 1.8;
  margin-bottom: 20px;
}

.spec-courses {
  list-style: none;
  margin: 0 0 24px;
  padding: 0;
}

.spec-course {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 0;
  border-top: 1px solid var(--line-mid);
}

.spec-course:last-child { border-bottom: 1px solid var(--line-mid); }

.spec-course-title {
  font-size: 12px;
  color: var(--secondary);
  line-height: 1.4;
}

.spec-course-code {
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--faint);
  white-space: nowrap;
  flex-shrink: 0;
}

.spec-count {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 20px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--line);
}

.spec-n    { font-size: 28px; font-weight: 500; color: var(--black); line-height: 1; transition: color 0.25s ease; }
.spec-unit { font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim); }

.spec-works { margin-bottom: 20px; }

.spec-work-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 8px;
  align-items: baseline;
  padding: 7px 0;
  border-bottom: 1px solid var(--line-mid);
  cursor: pointer;
  transition: opacity 0.1s;
}

.spec-work-row:hover { opacity: 0.7; }
.spec-work-row.ghost { opacity: 0.25; cursor: default; }
.spec-work-row.ghost:hover { opacity: 0.25; }

.spec-work-title   { font-size: 12px; color: var(--black); }
.spec-work-student { font-size: 11px; color: var(--secondary); white-space: nowrap; }
.spec-work-year    { font-size: 11px; color: var(--dim); white-space: nowrap; }

/* ── PANEL HIGHLIGHT STATES ──────────────────────── */

body[data-spec] .spec-panel {
  opacity: 0.25;
  transition: opacity 0.2s ease, background 0.2s ease;
}

body[data-spec] .spec-panel.is-lit {
  opacity: 1;
}

body[data-spec="research"]      .spec-panel.is-lit { background: var(--tint-research); }
body[data-spec="animation"]     .spec-panel.is-lit { background: var(--tint-animation); }
body[data-spec="games"]         .spec-panel.is-lit { background: var(--tint-games); }
body[data-spec="interactivity"] .spec-panel.is-lit { background: var(--tint-interactivity); }

body[data-spec="research"]      .spec-panel.is-lit .spec-title,
body[data-spec="research"]      .spec-panel.is-lit .spec-n { color: #5a6200; }

body[data-spec="animation"]     .spec-panel.is-lit .spec-title,
body[data-spec="animation"]     .spec-panel.is-lit .spec-n { color: #a0007a; }

body[data-spec="games"]         .spec-panel.is-lit .spec-title,
body[data-spec="games"]         .spec-panel.is-lit .spec-n { color: #007fa0; }

body[data-spec="interactivity"] .spec-panel.is-lit .spec-title,
body[data-spec="interactivity"] .spec-panel.is-lit .spec-n { color: #b04400; }

/* ── SECTION BAR ─────────────────────────────────── */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  height: 52px;
  border-bottom: 1px solid var(--line);
  border-top: 1px solid var(--line);
}

.bar-title {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

.bar-title-link {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--black);
  text-decoration: none;
  transition: opacity 0.1s;
}

.bar-title-link:hover { opacity: 0.5; }

/* dark treatment for news section bar */
#news .bar-title-link {
  color: #fff;
}

#news .bar-title-link:hover { opacity: 0.6; }
.bar-right { display: flex; align-items: center; height: 100%; }

.view-btn {
  font-size: 9px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim); padding: 0 12px; height: 100%;
  display: flex; align-items: center;
  border-left: 1px solid var(--line);
  cursor: pointer; transition: color 0.1s; user-select: none;
}

.view-btn:hover, .view-btn.active { color: var(--black); }

.bar-link {
  font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--dim); cursor: pointer; transition: color 0.1s;
  padding: 0 0 0 16px;
  border-left: 1px solid var(--line);
  height: 100%; display: flex; align-items: center;
}

.bar-link:hover { color: var(--black); }

/* ── FILTERS ─────────────────────────────────────── */

.filters {
  display: flex;
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}

.filters::-webkit-scrollbar { display: none; }

.filter-btn {
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--dim); padding: 0 16px; height: 32px;
  display: flex; align-items: center;
  border-right: 1px solid var(--line);
  cursor: pointer; white-space: nowrap;
  transition: color 0.1s; user-select: none; flex-shrink: 0;
}

.filter-btn:hover, .filter-btn.active { color: var(--black); }

/* ── WORKS GRID ──────────────────────────────────── */

#view-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  border-left: 1px solid var(--line);
}

.work-card {
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 0;
  cursor: pointer;
  transition: background 0.1s, opacity 0.2s;
  overflow: hidden;
}

.work-card:hover { background: var(--bg-hover); }
.work-card.ghost  { opacity: 0.2; cursor: default; }
.work-card.ghost:hover { background: none; }
.work-card.dimmed { opacity: 0.15; pointer-events: none; }

.card-thumb {
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--thumb-bg, #f0f0f0);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  transition: opacity 0.2s;
}

.card-thumb svg,
.card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.work-card:hover .card-thumb { opacity: 0.8; }

.card-body { padding: 12px 14px 14px; }
.card-meta { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim); margin-bottom: 5px; display: flex; justify-content: space-between; padding: 12px 14px 0; }
.card-title { font-size: 13px; color: var(--black); line-height: 1.4; margin-bottom: 4px; padding: 0 14px; font-weight: 500; }
.card-student { font-size: 11px; color: var(--secondary); margin-bottom: 10px; padding: 0 14px; }
.card-tags { display: flex; flex-wrap: wrap; gap: 3px; padding: 0 14px 14px; }
.tag { font-size: 9px; color: var(--muted); border: 1px solid var(--line); padding: 2px 6px; }

/* Staff tags — same solid category-badge style used on Alumni, Archive, etc. */
.staff-tags .tag {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 3px 8px;
  border: none;
}

.staff-card[data-cat="research"]      .staff-tags .tag { background: var(--col-research);      color: #4a5200; }
.staff-card[data-cat="animation"]     .staff-tags .tag { background: var(--col-animation);     color: #fff; }
.staff-card[data-cat="games"]         .staff-tags .tag { background: var(--col-games);         color: #003a4a; }
.staff-card[data-cat="interactivity"] .staff-tags .tag { background: var(--col-interactivity); color: #fff; }

/* ── SELECTED WORKS (framed cards) ───────────────── */

.sel-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 28px;
  padding: 36px 48px 44px;
}

.sel-card {
  display: block;
  text-decoration: none;
  color: inherit;
}

.sel-thumb {
  position: relative;
  aspect-ratio: 4 / 3;
  border: 5px solid var(--frame, var(--black));
  background: #f0f0f0;
  overflow: hidden;
  transition: transform 0.25s ease;
}

.sel-card:hover .sel-thumb { transform: translateY(-4px); }

.sel-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.sel-badge {
  position: absolute;
  top: 0;
  left: 0;
  background: var(--frame, var(--black));
  color: var(--frame-text, #fff);
  font-size: clamp(13px, 1.1vw, 16px);
  font-weight: 700;
  letter-spacing: 0.005em;
  padding: 3px 13px 5px;
  z-index: 2;
}

.sel-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 2px;
  padding: 16px 14px 12px;
  background: linear-gradient(to top, rgba(0,0,0,0.78) 0%, rgba(0,0,0,0) 55%);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.sel-card:hover .sel-overlay { opacity: 1; }

.sel-title {
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  line-height: 1.2;
}

.sel-student {
  font-size: 11px;
  letter-spacing: 0.04em;
  color: rgba(255,255,255,0.8);
}

.sel-card.dimmed { opacity: 0.15; pointer-events: none; }

/* Cards that open the detail panel below (currently just the featured Saros
   card) are buttons rather than links — reset the button chrome back to how
   the anchor cards look, and add a ring so the open one reads as selected. */
.sel-card-btn {
  position: relative;          /* anchors the open-state pointer below */
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}
.sel-card-btn .sel-thumb { transition: transform 0.25s ease, box-shadow 0.25s ease; }
.sel-card-btn[aria-expanded="true"] .sel-thumb {
  box-shadow: 0 0 0 3px var(--frame, var(--black));
}
.sel-card-btn[aria-expanded="true"] .sel-overlay { opacity: 1; }

/* The open card stays put instead of taking the hover lift. That lift moves
   only .sel-thumb, while the pointer below is anchored to the button — so on
   hover the card slid up and left a white sliver between its frame and the
   triangle. Anchoring the open card removes the cause; the overlap below
   covers the box-shadow ring's seam as well. */
.sel-card-btn[aria-expanded="true"] .sel-thumb { transform: none; }

/* Downward pointer marking which card is currently open. Takes the card's own
   frame colour, so it reads as belonging to that work. Its top is tucked 2px
   inside the card (14px tall, sitting 12px proud) so no gap can open up along
   the frame, and it still clears the 16px grid gap at the tightest breakpoint. */
.sel-card-btn::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -12px;
  width: 0;
  height: 0;
  border-left: 14px solid transparent;
  border-right: 14px solid transparent;
  border-top: 14px solid var(--frame, var(--black));
  transform: translateX(-50%) scaleY(0);
  transform-origin: top center;
  opacity: 0;
  transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}
.sel-card-btn[aria-expanded="true"]::after {
  opacity: 1;
  transform: translateX(-50%) scaleY(1);
}

/* ── FEATURED WORK DETAIL (expands under whichever card opened it) ──
   Lives INSIDE .sel-grid as a grid item (see the HTML) rather than after the
   grid as a section-level block. grid-column: 1 / -1 forces it to break onto
   its own full-width row wherever it sits in DOM order — and JS keeps it
   positioned right after the open card — so it appears directly under that
   card at every breakpoint, including the single-column mobile layout,
   instead of trailing after all four cards regardless of which was clicked. */
.sel-detail-wrap {
  grid-column: 1 / -1;
  /* display:none, not a 0fr grid — a collapsed grid item still occupies a
     full row, which broke the 4-across desktop layout into 2+2 even when
     shut. `is-shown` puts it back in flow, then `is-open` animates it; JS
     sequences the two so the open/close still eases. */
  display: none;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.34s cubic-bezier(0.4, 0, 0.2, 1);
}
.sel-detail-wrap.is-shown { display: grid; }
.sel-detail-wrap.is-open  { grid-template-rows: 1fr; }
.sel-detail { overflow: hidden; }

/* Switching straight from one featured work to another shouldn't replay the
   drop-down — the section is already open, only its contents are changing.
   `is-instant` kills the height transition so the swap doesn't collapse and
   re-expand, and `is-fading` cross-fades the contents across the swap. The
   drop-down itself is kept for opening from closed and for closing. */
.sel-detail-wrap.is-instant { transition: none; }
.sel-detail-wrap.is-fading .sel-detail-inner { opacity: 0; }

/* The grid's own gap supplies the space above and below, so this only needs
   to keep the text off the cards. */
.sel-detail-inner {
  display: flex;
  gap: 32px;
  padding: 4px 0 8px;
  opacity: 1;
  transition: opacity 0.16s ease;
}

/* No forced aspect-ratio and no object-fit: the thumbnail sets its own height
   from its natural proportions, so it is never cropped or stretched. The flex
   row would otherwise stretch this to the text column's height, hence
   align-self; line-height:0 stops the inline-image baseline gap. */
.sel-detail-video {
  position: relative;
  flex: 0 0 320px;
  align-self: flex-start;
  overflow: hidden;
  display: block;
  line-height: 0;
  background: #000;
}
.sel-detail-video img {
  width: 100%;
  height: auto;
  display: block;
  transition: transform 0.25s ease, opacity 0.2s ease;
}
.sel-detail-video:hover img { transform: scale(1.04); opacity: 0.85; }

.sel-detail-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  line-height: 1;              /* the wrapper zeroes it out for the image */
  color: #fff;
  background: rgba(0, 0, 0, 0.28);
  pointer-events: none;
}

.sel-detail-text { flex: 1; min-width: 0; }

.sel-detail-meta {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 10px;
}

/* The category · year, highlighted in the card's own frame colour. Padding
   and a negative margin keep the highlight from nudging surrounding text or
   growing the line height — same font-size as the rest of .sel-detail-meta,
   just with a coloured chip behind it. */
.sel-detail-meta-tag {
  background: var(--frame, var(--black));
  color: var(--frame-text, #fff);
  padding: 3px 6px;
  margin: -3px -2px;
  border-radius: 2px;
}

.sel-detail-name {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 2px;
}

.sel-detail-work {
  font-size: 13px;
  color: var(--dim);
  margin-bottom: 14px;
}

.sel-detail-bio {
  font-size: 14px;
  line-height: 1.75;
  color: var(--secondary);
  max-width: 62ch;
  margin-bottom: 18px;
}

.sel-detail-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--black);
  border: 1px solid var(--line);
  padding: 9px 14px;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.sel-detail-link:hover {
  background: var(--black);
  color: var(--white);
  border-color: var(--black);
}

@media (max-width: 880px) {
  .sel-grid { grid-template-columns: repeat(2, 1fr); gap: 20px; padding: 28px 24px 32px; }
  .sel-detail-inner { flex-direction: column; gap: 18px; }
  /* Stacked now, so align-self governs width rather than height — it must not
     stay flex-start or the thumbnail would shrink to its own content width. */
  .sel-detail-video { flex-basis: auto; width: 100%; align-self: stretch; }
}

/* ── WORKS LIST ──────────────────────────────────── */

#view-list { border-bottom: 1px solid var(--line); display: none; }

.list-row {
  display: grid;
  grid-template-columns: 40px 1fr 160px 110px 180px 52px;
  align-items: center;
  padding: 0 24px;
  height: 44px;
  border-bottom: 1px solid var(--line-mid);
  cursor: pointer;
  transition: background 0.1s, opacity 0.2s;
}

.list-row:hover { background: var(--bg-hover); }
.list-row:hover .col-title { color: var(--black); }
.list-row.head { height: 30px; cursor: default; border-bottom: 1px solid var(--line); }
.list-row.head:hover { background: none; }
.list-row.dimmed { opacity: 0.15; pointer-events: none; }

.col-head    { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--dim); }
.col-n       { font-size: 11px; color: var(--faint); }
.col-title   { font-size: 13px; color: var(--black); transition: color 0.1s; font-weight: 500; }
.col-student { font-size: 11px; color: var(--secondary); }
.col-type    { font-size: 11px; color: var(--muted); }
.col-tags    { display: flex; gap: 4px; flex-wrap: wrap; }
.col-year    { font-size: 11px; color: var(--dim); text-align: right; }
.list-tag    { font-size: 9px; letter-spacing: 0.04em; color: var(--muted); border: 1px solid var(--line); padding: 2px 6px; }

/* ── STUDENTS ────────────────────────────────────── */

.students-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  border-left: 1px solid var(--line);
}

.student-card {
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 22px 20px;
  cursor: pointer;
  transition: background 0.1s;
}

.student-card:hover  { background: var(--bg-hover); }
.student-card.ghost  { opacity: 0.2; cursor: default; }
.student-card.ghost:hover { background: none; }

.student-initials { font-size: 9px; font-weight: 500; color: var(--secondary); margin-bottom: 10px; letter-spacing: 0.06em; }
.student-name     { font-size: 13px; color: var(--black); margin-bottom: 2px; line-height: 1.3; }
.student-stream   { font-size: 11px; color: var(--muted); margin-bottom: 8px; }
.student-count    { font-size: 9px; color: var(--dim); }
.student-count em { color: var(--muted); font-style: normal; }

/* ── FOOTER ──────────────────────────────────────── */

.footer {
  background: #0a0a0a;
  color: #fff;
  border-top: 1px solid var(--line);
}

.footer-top {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  padding: 56px 48px 44px;
  gap: 32px;
  border-bottom: 1px solid #e0e0e0;
}

.footer-logos {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 14px;
}

.footer-magi {
  height: 40px;
  width: auto;
  display: block;
}

.footer-rmit {
  height: 40px;
  width: auto;
  display: block;
}

.footer-logos-divider {
  font-size: 20px;
  color: #444;
  font-weight: 300;
}

.footer-brand-sub {
  font-size: 11px;
  letter-spacing: 0.06em;
  color: #666;
  line-height: 1.6;
}

.footer-info-title {
  font-size: 14px;
  font-weight: 500;
  color: #fff;
  margin-bottom: 6px;
}

.footer-info-line {
  font-size: 12px;
  color: #888;
  line-height: 1.8;
}

.footer-apply-row {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 20px;
}

.footer-apply {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  transition: opacity 0.1s;
}

.footer-apply:hover { opacity: 0.6; }

.footer-follow {
  font-size: 11px;
  color: #666;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.footer-socials {
  display: flex;
  gap: 12px;
  align-items: center;
}

.footer-social {
  color: #888;
  text-decoration: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s;
}

.footer-social:hover { color: #fff; }

.footer-bottom {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 40px;
}

.footer-copy {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #444;
}

.footer-email {
  font-size: 12px;
  color: #666;
  text-decoration: none;
  transition: color 0.15s;
}

.footer-email:hover { color: #fff; }

/* ── PAGE HEADER ─────────────────────────────────── */

.page-header {
  padding: 48px 40px 40px;
  border-bottom: 1px solid var(--line);
}

.page-eyebrow {
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 10px;
}

.page-title {
  font-size: 36px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--black);
  margin-bottom: 10px;
  line-height: 1.1;
}

.page-desc {
  font-size: 14px;
  color: var(--secondary);
  line-height: 1.7;
  max-width: 560px;
}

/* ── STAFF GRID ──────────────────────────────────── */

.staff-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 28px;
  padding: 28px;
  background: var(--bg-hover);
  align-items: start;
}

/* Floats as its own card rather than a shared-border grid cell — matches
   the Sound Collab pamphlet treatment, but sized to its content, not a
   fixed ratio, so there's no dead space under a short bio. */
.staff-card {
  position: relative;
  border: 1px solid var(--line);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
  background: var(--white);
  padding: 26px 24px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  cursor: pointer;
  transition: background 0.1s, box-shadow 0.15s;
}

.staff-card:hover { background: var(--bg-hover); box-shadow: 0 4px 18px rgba(0, 0, 0, 0.09); }

.staff-initials {
  align-self: flex-start;
  width: 104px;
  height: 104px;
  border-radius: 50%;
  background: var(--black);
  color: var(--white);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.08em;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.staff-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  width: 100%;
}

.staff-photo {
  align-self: flex-start;
  width: 104px;
  height: 104px;
  border-radius: 50%;
  object-fit: cover;
  object-position: center top;
  flex-shrink: 0;
  display: block;
  filter: grayscale(1);
}

.staff-name {
  font-size: 15px;
  font-weight: 500;
  color: var(--black);
}

.staff-role {
  font-size: 12px;
  color: var(--secondary);
}

.staff-dept {
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 8px;
}

.staff-desc {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.5;
  margin: 8px 0 12px;
}

.staff-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 8px;
}

.staff-link {
  position: absolute;
  top: 20px;
  right: 20px;
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  text-decoration: none;
  background: var(--white);
  border: 1px solid var(--line);
  padding: 6px 10px;
  white-space: nowrap;
  transition: color 0.1s, border-color 0.1s;
  z-index: 2;
}

.staff-link:hover { color: var(--black); border-color: var(--black); }

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

@media (max-width: 480px) {
  .staff-grid { grid-template-columns: 1fr; gap: 20px; padding: 20px; }
  .staff-card { padding: 24px 22px; gap: 14px; }
  .staff-photo, .staff-initials { width: 76px; height: 76px; }
}

/* ── RECENT RECORDINGS SPOTLIGHT ────────────────── */

#recent-recordings {
  background: #0a0a0a;
}

.rec-dark-bar {
  background: #0a0a0a !important;
  border-bottom-color: #e0e0e0 !important;
  border-top-color: #e0e0e0 !important;
}

.rec-dark-bar .bar-title-link { color: #fff !important; }
.rec-dark-bar .bar-link { color: #666 !important; border-left-color: #e0e0e0 !important; }
.rec-dark-bar .bar-link:hover { color: #fff !important; }

.rec-spotlight {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-bottom: 1px solid #e0e0e0;
}

/* ── Featured item ───────────────────────────────── */

.rec-spotlight-feature {
  border-right: 1px solid #e0e0e0;
  display: flex;
  flex-direction: column;
}

.rec-spotlight-img {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  flex-shrink: 0;
}

.rec-spotlight-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: brightness(0.7);
  transition: filter 0.3s;
}

.rec-spotlight-feature:hover .rec-spotlight-img img {
  filter: brightness(0.85);
}

.rec-spotlight-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 52px;
  height: 52px;
  border: 2px solid rgba(255,255,255,0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  cursor: pointer;
  transition: background 0.2s;
}

.rec-spotlight-feature:hover .rec-spotlight-play {
  background: rgba(255,255,255,0.15);
}

.rec-spotlight-badge {
  position: absolute;
  top: 14px;
  left: 14px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 4px 8px;
}

.rec-spotlight-badge.workshop { background: var(--col-research); color: #4a5200; }
.rec-spotlight-badge.talk     { background: var(--col-games);    color: #003a4a; }
.rec-spotlight-badge.panel    { background: var(--col-animation); color: #fff; }
.rec-spotlight-badge.lecture  { background: var(--col-interactivity); color: #fff; }

.rec-spotlight-info {
  padding: 24px 28px 32px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}

.rec-spotlight-eyebrow {
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #666;
}

.rec-spotlight-title {
  font-size: 20px;
  font-weight: 500;
  color: #fff;
  line-height: 1.3;
}

.rec-spotlight-speaker {
  font-size: 12px;
  color: #888;
}

.rec-spotlight-desc {
  font-size: 12px;
  color: #777;
  line-height: 1.8;
  margin-top: 4px;
  flex: 1;
}

.rec-spotlight-link {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  margin-top: 8px;
  transition: opacity 0.1s;
  display: inline-block;
}

.rec-spotlight-link:hover { opacity: 0.6; }

/* ── List items ──────────────────────────────────── */

.rec-spotlight-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.rec-spotlight-row {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.1s;
  border-bottom: 1px solid #e0e0e0;
  border-left: 1px solid #e0e0e0;
  overflow: hidden;
}

.rec-spotlight-row:nth-child(3),
.rec-spotlight-row:nth-child(4) { border-bottom: none; }

.rec-spotlight-row:hover { background: #141414; }

.rec-spotlight-row-img {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  flex-shrink: 0;
  position: relative;
}

.rec-spotlight-row-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: brightness(0.75);
  transition: filter 0.3s;
}

.rec-spotlight-row:hover .rec-spotlight-row-img img {
  filter: brightness(0.9);
}

.rec-spotlight-row-body {
  padding: 12px 16px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.rec-spotlight-row-title {
  font-size: 13px;
  font-weight: 500;
  color: #fff;
  line-height: 1.3;
}

.rec-spotlight-row-meta {
  font-size: 10px;
  color: #666;
  letter-spacing: 0.04em;
}

.rec-spotlight-row-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 3px 7px;
}

.rec-spotlight-row-badge.workshop { background: var(--col-research); color: #4a5200; }
.rec-spotlight-row-badge.talk     { background: var(--col-games);    color: #003a4a; }
.rec-spotlight-row-badge.panel    { background: var(--col-animation); color: #fff; }
.rec-spotlight-row-badge.lecture  { background: var(--col-interactivity); color: #fff; }

/* ── RECENT RECORDINGS COMPACT LIST ─────────────── */

.rec-compact-list {
  border-bottom: 1px solid #e0e0e0;
}

.rec-compact-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 24px;
  border-bottom: 1px solid #e0e0e0;
  text-decoration: none;
  transition: background 0.1s;
  cursor: pointer;
}

.rec-compact-row:last-child { border-bottom: none; }
.rec-compact-row:hover { background: #141414; }

.rec-compact-num {
  font-size: 11px;
  color: #444;
  width: 24px;
  flex-shrink: 0;
  text-align: right;
}

.rec-compact-thumb {
  width: 72px;
  height: 46px;
  overflow: hidden;
  flex-shrink: 0;
}

.rec-compact-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: brightness(0.8);
  transition: filter 0.2s;
}

.rec-compact-row:hover .rec-compact-thumb img { filter: brightness(1); }

.rec-compact-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.rec-compact-title {
  font-size: 13px;
  font-weight: 500;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rec-compact-meta {
  font-size: 11px;
  color: #666;
  letter-spacing: 0.04em;
}

.rec-compact-badge {
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 3px 8px;
  flex-shrink: 0;
}

.rec-compact-badge.workshop { background: var(--col-research); color: #4a5200; }
.rec-compact-badge.talk     { background: var(--col-games);    color: #003a4a; }
.rec-compact-badge.panel    { background: var(--col-animation); color: #fff; }
/* media types — matched to the Archive badge colours */
.rec-compact-badge.recording { background: var(--col-games);        color: #003a4a; }
.rec-compact-badge.lecture   { background: var(--col-research);     color: #4a5200; }
.rec-compact-badge.video     { background: var(--col-animation);    color: #fff; }
.rec-compact-badge.audio     { background: var(--col-interactivity);color: #fff; }
.rec-compact-badge.document  { background: #0a0a0a;                 color: #fff; }
.rec-compact-badge.image     { background: #7c5cff;                 color: #fff; }

.rec-compact-dur {
  font-size: 11px;
  color: #555;
  flex-shrink: 0;
  text-align: right;
  min-width: 44px;
}

/* ── RECENT MEDIA — EXPANDABLE ROWS ──────────────────── */
.rec-item { border-bottom: 1px solid #e0e0e0; }
.rec-item:last-child { border-bottom: none; }
.rec-item .rec-compact-row { border-bottom: none; }
.rec-item.is-open .rec-compact-row { background: #141414; }

/* play-glyph placeholder thumb (video with no still image) */
.rec-thumb-play {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #1c1c1c;
  color: #777;
  font-size: 13px;
}
.rec-item.is-open .rec-thumb-play { color: #fff; }

.rec-media-loading, .rec-media-empty {
  padding: 32px 24px;
  text-align: center;
  font-size: 12px;
  color: #666;
  letter-spacing: 0.03em;
}

/* the expanding panel — same 0fr→1fr technique as the Archive */
.rec-compact-panel-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.32s cubic-bezier(0.4, 0, 0.2, 1);
  background: #0d0d0d;
}
.rec-item.is-open .rec-compact-panel-wrap { grid-template-rows: 1fr; }
.rec-compact-panel { overflow: hidden; min-height: 0; }

.rec-compact-panel-inner {
  padding: 6px 24px 22px 64px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
}
.rec-panel-abstract { font-size: 13px; line-height: 1.6; color: #aaa; max-width: 620px; }
.rec-panel-source { font-family: 'Teachers', serif; font-style: italic; font-size: 11px; color: #666; }
.rec-panel-link {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  border: 1px solid #333;
  padding: 9px 15px;
  margin-top: 2px;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.rec-panel-link:hover { background: #fff; color: #0a0a0a; border-color: #fff; }


/* Category description reveal — hidden entirely on desktop (see mobile block) */
.spec-mreveal { display: none; }

/* ══════════════════════════════════════════════════════
   MOBILE — home page + shared chrome
   ══════════════════════════════════════════════════════ */

/* Hamburger toggle — only present in the home nav for now, so the collapse
   behaviour is scoped to navs that actually contain it (see :has() below). */
.nav-toggle {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  margin-left: auto;
  background: none;
  border: none;
  border-left: 1px solid var(--line);
  cursor: pointer;
}
.nav-toggle span {
  display: block; width: 18px; height: 2px; background: var(--black);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav.open .nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav.open .nav-toggle span:nth-child(2) { opacity: 0; }
.nav.open .nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 760px) {

  /* ── NAV (collapsible — only where a hamburger exists) ──
     The bar's box stays locked at 56px so opening the menu never reflows the
     page. The panels still wrap onto their own flex lines, but those lines
     now overflow the fixed-height bar instead of growing it — so they paint
     over the page (the nav is sticky + z-indexed) rather than pushing it
     down. They carry their own background for that reason: they sit outside
     the bar's painted box. */
  .nav:has(.nav-toggle) {
    flex-wrap: wrap;
    align-items: center;
    align-content: flex-start;
    height: 56px;
    overflow: visible;
  }
  .nav:has(.nav-toggle) .nav-toggle { display: flex; }
  .nav:has(.nav-toggle) .nav-wordmark { height: 56px; }
  /* display:none can't be transitioned, so the collapse is driven by
     max-height + opacity instead — `display:none` becomes `visibility:hidden`,
     which *can* animate, via a transition-delay so it only takes effect once
     the collapse has actually finished (closing) or immediately (opening).
     That keeps the closed menu's links out of tab order, same as
     display:none did, without the hard visual cut. The 600px max-height is
     just a ceiling comfortably above the tallest real content (~400px) —
     grid-template-rows:0fr→1fr would size it exactly, but that needs an
     inner wrapper div this markup doesn't have. */
  .nav:has(.nav-toggle) .nav-links,
  .nav:has(.nav-toggle) .nav-right {
    flex-basis: 100%;
    flex-direction: column;
    height: auto;
    max-height: 0;
    visibility: hidden;
    overflow: hidden;
    border-top: 1px solid var(--line);
    background: var(--white);
    /* No opacity here on purpose — fading made it feel like it was
       materializing in place. Height is the only thing animating now, so it
       reads as unrolling downward instead. */
    transition: max-height 0.34s cubic-bezier(0.4, 0, 0.2, 1),
                visibility 0s linear 0.34s;
  }
  .nav:has(.nav-toggle).open .nav-links,
  .nav:has(.nav-toggle).open .nav-right {
    max-height: 600px;
    visibility: visible;
    transition: max-height 0.34s cubic-bezier(0.4, 0, 0.2, 1),
                visibility 0s linear 0s;
  }
  /* nav-right is the lower of the two stacked panels, so it's what casts the
     menu's shadow onto the page underneath. */
  .nav:has(.nav-toggle).open .nav-right { box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08); }
  .nav:has(.nav-toggle) .nav-right { margin-left: 0; }
  .nav:has(.nav-toggle) .nav-link {
    width: 100%;
    height: auto;
    padding: 15px 20px;
    border-right: none;
    border-left: none;
    border-bottom: 1px solid var(--line);
  }

  /* ── SEARCH — lives in the bar beside the hamburger, not in the menu ──
     The markup has it after .nav-right (so desktop's margin-left:auto still
     carries both to the right edge); order lifts it onto the bar's own flex
     line here. The auto margin moves off .nav-toggle onto .nav-search so the
     two travel right together as a pair. */
  .nav:has(.nav-toggle) .nav-wordmark { order: 1; }
  .nav:has(.nav-toggle) .nav-search   { order: 2; margin-left: auto; }
  .nav:has(.nav-toggle) .nav-toggle   { order: 3; margin-left: 0; }
  .nav:has(.nav-toggle) .nav-links    { order: 4; }
  .nav:has(.nav-toggle) .nav-right    { order: 5; }

  .nav:has(.nav-toggle) .nav-search {
    width: auto;
    height: 56px;
    border-left: none;
    border-bottom: none;
  }
  /* Icon only — no room for the word beside the hamburger. The icon is
     absolutely stacked over the label by default; with the label gone it has
     to go back in flow or it'd collapse to nothing. */
  .nav:has(.nav-toggle) .nav-search-label { display: none; }
  .nav:has(.nav-toggle) .nav-search-icon { position: static; opacity: 1; }
  .nav:has(.nav-toggle) .nav-search-btn { padding: 0 16px; flex-shrink: 0; }
  /* Open: take the room between wordmark and toggle. A fixed 200px input (the
     desktop figure) would overflow a 320px bar, so grow into what's there.
     margin-left has to drop back to 0 — an auto margin eats the free space
     before flex-grow ever sees it. */
  .nav:has(.nav-toggle) .nav-search.open { flex: 1; margin-left: 0; }
  .nav:has(.nav-toggle) .nav-search.open .nav-search-input {
    flex: 1;
    width: auto;
    min-width: 0;
    padding: 0 4px 0 14px;
  }

  /* EXPO becomes a tap-to-expand accordion row instead of a hover flyout */
  .nav:has(.nav-toggle) .nav-expo {
    flex-direction: column;
    align-items: stretch;
    height: auto;
    width: 100%;
    position: relative;
  }
  .nav:has(.nav-toggle) .nav-expo-link { border-bottom: 1px solid var(--line); }
  .nav:has(.nav-toggle) .nav-expo-menu {
    position: static;
    display: none;
    opacity: 1;
    pointer-events: auto;
    transform: none;
    border: none;
    box-shadow: none;
    background: var(--bg-hover);
    width: 100%;
  }
  /* The menu now overlays the page rather than lengthening it, so it can't be
     scrolled to — everything has to fit the viewport. The nav rows alone do
     (~470px), but expanding EXPO in place would push the last items off a
     short screen, so it scrolls within its own row instead. */
  .nav:has(.nav-toggle) .nav-expo.open .nav-expo-menu {
    display: block;
    max-height: 152px;
    overflow-y: auto;
  }
  .nav:has(.nav-toggle) .nav-expo-item { padding-left: 32px; }
  /* the desktop "meets the dropdown" accent line makes no sense against a
     full-width accordion row on mobile */
  .nav:has(.nav-toggle) .nav-expo-accent { display: none; }

  /* ── HERO ─────────────────────────────────────────── */
  /* Sized to the content (logo + tagline ≈ 190px) rather than a big desktop
     vh figure, which left the short mobile logo stranded in empty space. */
  .hero { height: 52vh; min-height: 380px; max-height: 540px; }
  .hero-logo { top: 42%; width: 94%; }
  .logo-wrap { gap: clamp(4px, 1.4vw, 12px); }
  /* The letters are sized by HEIGHT (keeping each one's own proportions), but
     what we actually want to control is how wide the MAGI row lands. The four
     aspect ratios sum to 3.583, so:
         row = height × 3.583 + 3 gaps
     Solving for a row ≈ 90vw, with the ~1.4vw gaps above:
         height × 3.583 + 4.2vw = 90vw  →  height ≈ 24vw
     That holds its span across the whole mobile range (320 → 760px) without a
     clamp, which is what was shrinking it to ~54% of the screen before. */
  .logo-letter { height: 24vw; }
  .hero-tagline-wrap { bottom: 44px; width: 92%; gap: 14px; }
  .hero-tagline-wrap .hero-rmit { height: 38px; }

  /* ── SPECIALISATIONS — compact logo row + tap-to-reveal ── */
  .specs {
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: auto;
    border-left: none;
  }
  .spec-panel { cursor: pointer; }         /* keeps the coloured ::before top bar */
  .spec-header {
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
    padding: 16px 4px 14px;
    border-bottom: none;
  }
  .spec-symbol { height: 42px; }
  .spec-header-label {
    align-self: center;                    /* override base flex-end so it centres under the logo */
    font-size: 9px;
    letter-spacing: 0.04em;
    padding: 3px 6px 4px;
    text-align: center;
    line-height: 1.2;
  }
  .spec-panel.is-open { background: var(--bg-hover); }
  .spec-panel.is-open .spec-symbol { filter: none; }
  .spec-inner { display: none; }                    /* desc shown in the shared reveal */

  .spec-mreveal {
    grid-column: 1 / -1;
    display: none;
    padding: 20px 22px 24px;
    border-top: 1px solid var(--line);
    background: var(--bg-hover);
  }
  .spec-mreveal.show { display: block; }
  .spec-mreveal .spec-desc { font-size: 13px; color: var(--secondary); line-height: 1.6; }

  /* ── UPLOAD CTA ─────────────────────────────────────
     Drops below News & Events here, and drops its voice with it: the pale
     yellow fill and big type read as a banner on a narrow screen, which is
     more weight than a secondary action wants. */

  /* Reordering needs a flex parent. `:has(#upload)` keeps that scoped to the
     home page — the other eight pages stay plain block layout. None of these
     sections carry vertical margins, so flex is layout-equivalent here. */
  body:has(#upload) { display: flex; flex-direction: column; }
  /* Every child needs a number: order groups run 0s first, so numbering only
     #upload would fling it past the footer. */
  body:has(#upload) > .nav               { order: 0; }
  body:has(#upload) > .hero              { order: 1; }
  body:has(#upload) > #specialisations   { order: 2; }
  body:has(#upload) > #news              { order: 3; }
  body:has(#upload) > #upload            { order: 4; }
  body:has(#upload) > #archive           { order: 5; }
  body:has(#upload) > #recent-recordings { order: 6; }
  body:has(#upload) > #gallery           { order: 7; }
  body:has(#upload) > .footer            { order: 8; }

  /* Grey rather than white: it now sits between the black News section and
     the light Archive, so white would dissolve into the latter. This keeps it
     a quiet band instead of a yellow banner. */
  .upload-cta { background: var(--bg-hover); }
  .upload-inner { padding: 26px 22px; gap: 14px; }
  .upload-title { font-size: 14px; margin-bottom: 3px; }
  .upload-desc  { font-size: 11px; line-height: 1.6; }
  .upload-btn   { font-size: 10px; padding: 11px 20px; }

  /* ── NEWS & EVENTS — horizontal scroller ──────────── */
  .news-grid {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;                 /* Firefox */
    /* The desktop rule pins this to 40/9 so the 2fr-of-5 featured column
       lands at the video's own 16:9. That fraction is meaningless here —
       cards are 80% width each, stacked in a scroller, not sharing one grid
       row — so it's released back to natural, per-card content height. */
    aspect-ratio: auto;
    /* Same reasoning: the desktop min-height is a floor against the
       aspect-ratio-driven row shrinking too far; there's no shared row here
       to floor, cards just take whatever their own content needs. */
    min-height: 0;
  }
  .news-grid::-webkit-scrollbar { display: none; }  /* WebKit */
  .news-card {
    --news-pad-x: 22px;
    --news-pad-y: 24px;
    flex: 0 0 80%;
    min-width: 0;
    scroll-snap-align: start;
    /* Desktop clips + line-clamps text to fit the pinned row height; the
       scroller has no such constraint, so give the full copy back its room.
       :not(.news-card-featured) — that card still needs overflow:hidden to
       contain the ambient video (see .news-card-featured), which otherwise
       loses to this rule on specificity ties + source order. */
  }
  /* .news-thumb keeps its own overflow:hidden regardless (it's what clips the
     image/video to the thumb box); this only relaxes the CARD's overflow,
     which existed to stop text overrunning the fixed desktop row height —
     moot here since the card's height is auto again (see .news-grid above).
     news-card-featured is excluded: it still needs to clip so its
     .news-more-wrap overlay doesn't visually spill past the card corners. */
  .news-card:not(.news-card-featured) { overflow: visible; }
  .news-card:last-child { border-right: none; }

  /* The desktop rule's flex:0 0 65% is a PERCENTAGE flex-basis, which only
     resolves against a definite container height — the card here is auto
     height (see .news-grid above), so that percentage can't resolve and
     falls back to being treated as flex-basis:auto. Since .news-thumb's only
     content is an absolutely-positioned image (no in-flow height at all),
     that collapsed it to near-zero — the actual cause of the mobile squish:
     a barely-visible thumbnail with .news-desc's now-unclamped text filling
     all the space. flex:0 0 auto + a fixed aspect-ratio gives it a real,
     self-contained size instead of depending on a percentage the layout
     can't provide here. */
  .news-thumb {
    flex: 0 0 auto;
    aspect-ratio: 3 / 2;
  }
  .news-desc {
    /* display:block (rather than just resetting line-clamp) is what actually
       undoes the desktop rule — -webkit-line-clamp only has an effect inside
       display:-webkit-box, so leaving that in place and merely changing the
       clamp value is unreliable across browsers. */
    display: block;
    overflow: visible;
  }

  /* ── NEWS & EVENTS — swipe-hint dots ──────────────────
     A minimal row of dots under the scroller so it reads as a swipeable
     carousel rather than a row that just happens to be clipped. The active
     dot is driven by the scroller position in the script (adds .is-active). */
  .news-dots {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 8px;
    padding: 16px 0 18px;
    background: #0a0a0a;
    border-bottom: 1px solid #e0e0e0;
  }
  .news-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #3a3a3a;
    padding: 0;
    border: none;
    cursor: pointer;
    transition: background 0.2s ease, transform 0.2s ease;
  }
  .news-dot.is-active {
    background: #e0e0e0;
    transform: scale(1.35);
  }

  /* ── SELECTED WORKS ───────────────────────────────── */
  .sel-grid { grid-template-columns: 1fr 1fr; gap: 16px; padding: 22px; }

  /* ── RECENT MEDIA ─────────────────────────────────── */
  .rec-compact-row { padding: 12px 18px; gap: 12px; }
  .rec-compact-num { display: none; }
  .rec-compact-panel-inner { padding: 6px 18px 20px 18px; }

  /* ── GALLERY ──────────────────────────────────────── */
  /* Two columns instead of three. No aspect-ratio override here — forcing one
     would crop the images again, which is the thing this layout exists to
     avoid; heights stay natural at every width. */
  .gallery-grid {
    columns: 2;
    column-gap: 16px;
    padding: 24px 24px 4px;
  }
  .gallery-item { margin-bottom: 26px; }
  .gallery-item-label { font-size: 9px; margin-top: 11px; }
  .gallery-item-title { font-size: 15px; margin-top: 4px; }
  .gallery-item-student { font-size: 10px; margin-top: 4px; }

  /* ── FOOTER ───────────────────────────────────────── */
  .footer-top { grid-template-columns: 1fr; padding: 40px 24px 32px; gap: 28px; }
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: 8px; padding: 16px 24px; }
}

@media (max-width: 480px) {
  .sel-grid { grid-template-columns: 1fr; }
  /* No .logo-letter override here — the 24vw above already scales linearly all
     the way down, and re-clamping it was what shrank the logo on small phones. */
  .hero-tagline-wrap .hero-badge { font-size: 12px; letter-spacing: 0.06em; }
  .hero-tagline-wrap .strip-tagline { font-size: 12px; }
}
/* ── SEARCH RESULTS PANEL ─────────────────────────────
   Full-viewport results below the nav, driven by the nav search box.
   Injected by search.js, so no per-page markup. `no-page-fade` opts it
   out of the global page fade-in (which would fight its own opacity). */

.search-results {
  position: fixed;
  top: 56px;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--white);
  z-index: 150;
  overflow-y: auto;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-6px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.search-results.open { opacity: 1; pointer-events: auto; transform: translateY(0); }

/* Freeze the page behind the results rather than letting both scroll */
body.search-open { overflow: hidden; }

.search-results-inner { max-width: 860px; margin: 0 auto; padding: 40px 32px 80px; }

.search-results-title {
  font-size: 27px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--black);
  margin-bottom: 26px;
}

/* ── Tabs (All + whichever groups matched) ── */
.search-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 26px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 8px;
}
.search-tab {
  position: relative;
  background: none;
  border: none;
  padding: 0 0 12px;
  font-family: 'Satoshi', sans-serif;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
  cursor: pointer;
  transition: color 0.1s;
  white-space: nowrap;
}
.search-tab:hover { color: var(--black); }
.search-tab.is-active { color: var(--black); }
.search-tab.is-active::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  background: var(--black);
}
.search-tab-n { color: var(--faint); font-weight: 400; margin-left: 2px; }

.search-note {
  font-size: 11px;
  line-height: 1.6;
  color: var(--muted);
  background: var(--bg-hover);
  border: 1px solid var(--line);
  padding: 10px 14px;
  margin-top: 18px;
}
.search-note a { color: var(--black); }

/* ── Groups ── */
.search-group { margin-top: 34px; }

.search-group-head {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--black);
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
}

/* ── Result row ── */
.sr-row {
  display: grid;
  grid-template-columns: 92px 1fr 16px;
  align-items: center;
  gap: 20px;
  padding: 18px 8px 18px 0;
  border-bottom: 1px solid var(--line);
  text-decoration: none;
  transition: background 0.1s;
}
.sr-row:hover { background: var(--bg-hover); }

.sr-thumb {
  width: 92px;
  height: 66px;
  overflow: hidden;
  background: #f0f0f0;
  flex-shrink: 0;
}
.sr-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Generated placeholder for results with no photo — same black-tile,
   initials-in-the-middle treatment as .staff-initials on the Staff page,
   just squared off to match this thumb slot instead of a circle. */
.sr-thumb-initials {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--black);
  color: var(--white);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.04em;
}

.sr-body { min-width: 0; }

.sr-title {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--black);
  line-height: 1.3;
}
.sr-kind { font-size: 11px; color: var(--secondary); margin-top: 3px; }
.sr-desc {
  font-size: 12px;
  color: var(--muted);
  line-height: 1.6;
  margin-top: 6px;
  /* keep long abstracts from bloating the row */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.sr-chev { color: var(--faint); flex-shrink: 0; }
.sr-row:hover .sr-chev { color: var(--black); }

.search-empty { font-size: 13px; color: var(--muted); padding: 40px 0; }

@media (max-width: 640px) {
  .search-results-inner { padding: 28px 20px 60px; }
  .search-results-title { font-size: 21px; }
  .search-tabs { gap: 18px; }
  .sr-row { grid-template-columns: 64px 1fr 16px; gap: 14px; }
  .sr-thumb { width: 64px; height: 48px; }
  .sr-thumb-initials { font-size: 12px; }
}
