/* ------------------------------------------------------------------
   Ubicomp Lab — design tokens + theming
   Dark mode is driven by `html.dark` (set by static/js/site.js).

   Design system layers (low → high):
     1. Brand        — immutable, university-defined colours
     2. Theme        — light/dark surfaces, text, borders
     3. Accent       — role-coded secondary hues
     4. Scale        — typography, spacing, radius, motion, elevation
     5. Components   — .editorial-row, .kicker, .topic-pill, .page-hero, etc.
   Templates should only consume layers 2–5; never hardcode brand hex.
   ------------------------------------------------------------------ */
@custom-variant dark (&:where(.dark, .dark *));

/* -------- Layer 1: Brand (theme-invariant) -------- */
:root {
  --brand-blue:      #1D428A;   /* Marquette Blue (web-friendly) */
  --brand-blue-deep: #003366;   /* Marquette Primary Blue — marquette.edu --primary_blue */
  --brand-blue-tint: #E8EEF9;   /* Marquette Blue washed */
  --brand-gold:      #FFCC00;   /* Marquette Gold — marquette.edu --primary_gold */
  --brand-gold-bright: #FCE701; /* light end of the marquette.edu gold gradient */
  --brand-gold-deep: #8A6400;   /* Gold text on light backgrounds */

  /* marquette.edu 2024 gradient family. Recipes lifted from
     marquette.edu/_resources-2024/css/global.css:
       --gradient_gold_1:  to right, #FCE701 → #FFCC00
       band blues:         #00CDFF / #009FE7 / #0072CE over #003366
       .h1 text gradient:  to right, #0098CA 10%, #003366 20%
     Negative start stops keep the visible surface navy-dominant so white
     type stays ≥ 5:1 everywhere on the gradient. */
  --grad-gold:      linear-gradient(to right, #FCE701, #FFCC00);
  --grad-navy-band: linear-gradient(105deg, #0072CE -14%, #003366 44%, #003366 100%);
  --grad-navy-card: linear-gradient(145deg, #0072CE -32%, #003366 58%);
  --grad-title:     linear-gradient(to right, #0098CA 0%, #003366 26%, #003366 100%);

  /* Third-party brand accents (reserved for share buttons, platform icons). */
  --brand-linkedin:  #0A66C2;
  --brand-facebook:  #1877F2;
  --brand-scholar:   #4285F4;
  --brand-twitter:   #0F172A;
  --brand-website:   #0F766E;
}

:root {
  --font-display: 'Fraunces', ui-serif, Georgia, serif;
  --font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
  /* Numerals — geometric grotesk for stat counters; distinct from the serif
     display face so numbers read as data, not prose. */
  --font-numeric: 'Space Grotesk', 'Inter', ui-sans-serif, sans-serif;

  /* Surfaces — cool navy-cast neutrals so the solid navy/gold cards sit in
     the same temperature as the page instead of jumping off stark white. */
  --bg:          #f1f5fa;   /* page background */
  --bg-elevated: #ffffff;   /* cards / panels */
  --surface-2:   #e7edf5;   /* subtle alternating band — slightly darker for contrast */
  --border:      #cdd7e4;   /* darker than slate-200 so cards read as cards */
  --border-strong: #94a3b8;

  /* Text — bumped contrast for light mode. Previous values (#475569 muted,
     #64748b subtle) were close to WCAG AA threshold on --bg; these pass
     comfortably against both --bg and --bg-elevated. */
  --fg:        #0b1324;     /* near-black, not pure black */
  --fg-muted:  #334155;     /* slate-700 */
  --fg-subtle: #475569;     /* slate-600 */

  /* Brand — Marquette University palette.
     Primary:   Marquette Blue   (PMS 288C ≈ #0C2340 deep, #1D428A web)
     Accent:    Marquette Gold   (PMS 123C ≈ #FFC72C)
     See marquette.edu and the MU brand guide for colour usage. */
  --primary:     #1D428A;   /* Marquette Blue (web-friendly) */
  --primary-hi:  #0C2340;   /* Marquette Blue (deep) — hover */
  --primary-lo:  #E8EEF9;   /* Marquette Blue tint */

  /* Accents — one navy→cerulean→gold family so every content area stays
     inside the Marquette brand instead of competing with it.
     Class/var names are legacy; only the values were remapped:
       emerald → lapis #05668D, rose → royal navy #023E8A,
       cyan → cerulean #0081A7, violet → royal navy, amber → deep gold. */
  --accent-amber:   #B78400;   /* gold as *text/icon* on light surfaces (AA) */
  --accent-emerald: #05668D;   /* projects / active — deep lapis */
  --accent-rose:    #023E8A;   /* grants — royal navy */
  --accent-cyan:    #0081A7;   /* publications — cerulean */
  --accent-violet:  #023E8A;   /* people — royal navy */
  --accent-indigo:  #1D428A;   /* links in prose — matches primary */

  /* Radius / shadow */
  --radius-sm: 0.5rem;
  --radius:    0.85rem;
  --radius-lg: 1.25rem;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgb(15 23 42 / 0.04);
  --shadow:    0 10px 30px -12px rgb(15 23 42 / 0.12),
               0 4px 10px  -6px rgb(15 23 42 / 0.06);
  --shadow-xl: 0 25px 60px -20px rgb(15 23 42 / 0.25);

  /* Semantic elevation aliases — prefer these over raw --shadow-*. */
  --elev-flat:    none;
  --elev-card:    var(--shadow-sm);
  --elev-hover:   var(--shadow);
  --elev-panel:   var(--shadow-xl);

  /* -------- Type scale (consume in components, not templates) -------- */
  --fs-display:  clamp(3rem,   5vw,   4.5rem);
  --fs-h1:       clamp(2.25rem,3.5vw, 3rem);
  --fs-h2:       1.75rem;
  --fs-h3:       1.25rem;
  --fs-body:     1rem;
  --fs-caption:  0.8125rem;
  --fs-eyebrow:  0.75rem;
  --tracking-eyebrow: 0.18em;
  --lh-tight:    1.15;
  --lh-body:     1.65;

  /* -------- Spacing scale -------- */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs:  0.75rem;
  --space-sm:  1rem;
  --space-md:  1.5rem;
  --space-lg:  2.5rem;
  --space-xl:  4rem;
  --space-2xl: 6rem;

  --container-narrow:   768px;
  --container-standard: 1280px;
  --container-wide:     1440px;
  --section-py:         clamp(3rem, 6vw, 6rem);

  /* -------- Motion -------- */
  --ease-out:          cubic-bezier(.2, .8, .2, 1);
  --ease-in-out:       cubic-bezier(.4, 0, .2, 1);
  --duration-fast:     150ms;
  --duration-standard: 300ms;
  --duration-slow:     700ms;

  /* -------- Focus ring (accessibility) -------- */
  --focus-ring:       2px solid var(--brand-blue);
  --focus-offset:     2px;
}

html.dark {
  --bg:          #0b1220;
  --bg-elevated: #111827;
  --surface-2:   #0f172a;
  --border:      #1f2937;
  --border-strong: #334155;

  --fg:        #e5e7eb;
  --fg-muted:  #cbd5e1;
  --fg-subtle: #94a3b8;

  /* Dark-mode variants — lighten each hue so it stays readable on the
     navy/slate background. Gold stays vivid; blue lifts to a friendlier sky. */
  --primary:     #60A5FA;   /* blue-400 — Marquette Blue lifted */
  --primary-hi:  #93C5FD;
  --primary-lo:  #1E3A8A;

  --accent-amber:   #FFC300;   /* gold lifted */
  --accent-emerald: #4FA8CC;   /* lapis lifted */
  --accent-rose:    #6D9EF1;   /* royal navy lifted */
  --accent-cyan:    #38BDE8;   /* cerulean lifted */
  --accent-violet:  #7CA8F4;   /* royal navy lifted (people) */
  --accent-indigo:  #93C5FD;

  --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.5);
  --shadow:    0 10px 30px -12px rgb(0 0 0 / 0.6),
               0 4px 10px  -6px rgb(0 0 0 / 0.5);
  --shadow-xl: 0 25px 60px -20px rgb(0 0 0 / 0.75);
}

html        { font-family: var(--font-sans); scroll-behavior: smooth; }
body        { background: var(--bg); color: var(--fg); transition: background-color .2s ease, color .2s ease; }
.font-display { font-family: var(--font-display); letter-spacing: -0.01em; }

/* Brand logo mark — light/dark swap (navy U hides on dark surfaces). */
.brand-logo--dark { display: none; }
html.dark .brand-logo { display: none; }
html.dark .brand-logo--dark { display: block; }

/* ---- Surface helper classes used in templates ---------------------- */
.surface           { background: var(--bg-elevated); }
.surface-alt       { background: var(--surface-2); }
.text-fg           { color: var(--fg); }
.text-fg-muted     { color: var(--fg-muted); }
.text-fg-subtle    { color: var(--fg-subtle); }
.text-brand        { color: var(--primary); }
.border-subtle     { border-color: var(--border); }
.border-strong     { border-color: var(--border-strong); }

.card {
  /* Solid card surface — flat elevated panel with a crisp border. Colour
     comes from role-coded content (kickers, chips), never from washes or
     inner highlights on the surface itself. */
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
  --role: var(--primary);
}
.card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
  border-color: color-mix(in oklab, var(--role) 45%, var(--border));
}

/* ------------------------------------------------------------------
   Stat card — Marquette "by the numbers" treatment. All six cards are
   the same solid deep-navy block with gold numerals and a short gold
   rule at the top: one flat, committed colour, no washes or halos.
   The row reads as a single cohesive band, exactly like the numbers
   sections on marquette.edu.
   ------------------------------------------------------------------ */
.stat-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  padding: 1.3rem 1.35rem 1.15rem;
  border-radius: var(--radius-lg);
  color: #fff;
  background: var(--brand-blue-deep);
  border: 1px solid color-mix(in oklab, var(--brand-gold) 18%, var(--brand-blue-deep));
  transition: transform .25s var(--ease-out), border-color .25s ease;
  overflow: hidden;
}
/* Short flat gold rule anchored top-left — the Marquette underline motif. */
.stat-card::before {
  content: "";
  position: absolute;
  top: 0; left: 1.35rem;
  width: 2.5rem; height: 3px;
  background: var(--brand-gold);
}
.stat-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklab, var(--brand-gold) 45%, var(--brand-blue-deep));
}
html.dark .stat-card {
  /* Same solid navy in dark mode; a hairline gold border keeps the block
     from merging with the page background. */
  border-color: color-mix(in oklab, var(--brand-gold) 28%, var(--brand-blue-deep));
}

.stat-card__row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}
.stat-card__icon {
  display: inline-grid; place-items: center;
  width: 2rem; height: 2rem;
  border-radius: var(--radius-sm);
  background: rgb(255 255 255 / 0.08);
  color: var(--brand-gold);
  flex-shrink: 0;
}
.stat-card__value {
  font-family: var(--font-numeric);
  font-weight: 700;
  font-size: clamp(2.1rem, 2.6vw, 2.6rem);
  line-height: 1;
  color: var(--brand-gold);
  letter-spacing: -0.02em;
  font-variant-numeric: lining-nums tabular-nums;
}
.stat-card__label {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: rgb(255 255 255 / 0.78);
}


/* Content card — consistent card used by Project, Grant, News listings.
   Thin top accent ribbon in the role colour plus image-zoom-on-hover.
   Shares the same premium gradient + inner-highlight as .card. */
.content-card {
  /* Solid deep-navy project card — same committed colour block as the stat
     band and director panel. White type, gold accents. */
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--brand-blue-deep);
  border: 1px solid color-mix(in oklab, var(--brand-gold) 22%, var(--brand-blue-deep));
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform .3s ease, border-color .3s ease;
  --role: var(--primary);
}
.content-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklab, var(--brand-gold) 50%, var(--brand-blue-deep));
}
.content-card__media {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--surface-2);
  /* Slim frame + flat Marquette-gold baseline under the image. Margin
     insets the media from the card edges so it reads as a framed figure,
     not a bleed-to-edge header. */
  margin: 10px 10px 0 10px;
  border-radius: calc(var(--radius) - 4px);
  border: 1px solid rgb(255 255 255 / 0.2);
  box-shadow: 0 3px 0 0 var(--brand-gold);
  transition: border-color var(--duration-standard) var(--ease-out);
}
.content-card:hover .content-card__media {
  border-color: rgb(255 255 255 / 0.4);
}
.content-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}
.content-card:hover .content-card__media img { transform: scale(1.05); }
.content-card__media-fallback {
  width: 100%; height: 100%;
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-size: 2.75rem;
  font-weight: 600;
  color: #fff;
  /* Solid role-coloured panel, anchored toward deep navy so every
     fallback stays in the Marquette family. */
  background: color-mix(in oklab, var(--role) 80%, var(--brand-blue-deep));
}
/* Default-artwork variant — flat brand illustration replaces the initial. */
.content-card__media-fallback--projects {
  background-image: url("../img/defaults/web/projects.jpg");
  background-size: cover;
  background-position: center;
}
.content-card__body { padding: var(--space-sm); display: flex; flex-direction: column; gap: 0.5rem; flex: 1; }
.content-card__title {
  font-weight: 600; color: #fff;
  line-height: 1.3;
  font-size: 1.05rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.content-card__excerpt {
  font-size: var(--fs-caption);
  color: rgb(255 255 255 / 0.75);
  line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.content-card__footer {
  margin-top: auto;
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 0.3rem;
}
.content-card__readmore {
  display: inline-flex; align-items: center; gap: 0.25rem;
  font-size: var(--fs-caption); font-weight: 600;
  color: var(--brand-gold);
}
.content-card__readmore svg { transition: transform var(--duration-fast) var(--ease-out); }
.content-card:hover .content-card__readmore svg { transform: translateX(3px); }

.btn-primary {
  background: var(--primary);
  color: white;
  padding: 0.65rem 1.1rem;
  border-radius: var(--radius-sm);
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  transition: background-color .2s ease, transform .1s ease;
}
.btn-primary:hover   { background: var(--primary-hi); }
.btn-primary:active  { transform: translateY(1px); }
/* Dark mode: --primary lifts to sky-blue where white text fails AA; switch
   to the on-brand gold button with navy text (8.8:1). */
html.dark .btn-primary        { background: var(--brand-gold); color: var(--brand-blue-deep); }
html.dark .btn-primary:hover  { background: #FFC300; }

.btn-ghost {
  padding: 0.6rem 1rem;
  border-radius: var(--radius-sm);
  font-weight: 500;
  color: var(--fg);
  border: 1px solid var(--border);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}
.btn-ghost:hover { background: var(--surface-2); }

/* Chips used for filters / tags ------------------------------------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.2rem 0.7rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 500;
  background: var(--surface-2);
  color: var(--fg-muted);
  border: 1px solid var(--border);
}
.chip-indigo  { background: color-mix(in oklab, var(--primary) 12%, transparent); color: var(--primary); border-color: color-mix(in oklab, var(--primary) 30%, transparent); }
.chip-amber   { background: color-mix(in oklab, var(--accent-amber) 15%, transparent);   color: var(--accent-amber);   border-color: color-mix(in oklab, var(--accent-amber) 35%, transparent); }
.chip-emerald { background: color-mix(in oklab, var(--accent-emerald) 15%, transparent); color: var(--accent-emerald); border-color: color-mix(in oklab, var(--accent-emerald) 35%, transparent); }
.chip-rose    { background: color-mix(in oklab, var(--accent-rose) 15%, transparent);    color: var(--accent-rose);    border-color: color-mix(in oklab, var(--accent-rose) 35%, transparent); }
.chip-cyan    { background: color-mix(in oklab, var(--accent-cyan) 15%, transparent);    color: var(--accent-cyan);    border-color: color-mix(in oklab, var(--accent-cyan) 35%, transparent); }
.chip-violet  { background: color-mix(in oklab, var(--accent-violet) 15%, transparent);  color: var(--accent-violet);  border-color: color-mix(in oklab, var(--accent-violet) 35%, transparent); }

/* Scroll reveal — progressive enhancement.
   Default state is fully visible (opacity: 1). At page load JS adds the
   `.js-reveal` class to every [data-reveal] element below the fold and
   then removes it as each enters the viewport. This way, if JS fails or
   the IntersectionObserver misfires, content is still visible. */
[data-reveal].js-reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .7s ease, transform .7s ease;
}
[data-reveal][data-reveal-delay="1"] { transition-delay: 60ms; }
[data-reveal][data-reveal-delay="2"] { transition-delay: 120ms; }
[data-reveal][data-reveal-delay="3"] { transition-delay: 180ms; }
[data-reveal][data-reveal-delay="4"] { transition-delay: 240ms; }
[data-reveal][data-reveal-delay="5"] { transition-delay: 300ms; }

/* Colourful hero backdrop — decorative gradient orbs.
   Safe to apply to any container: only adds pseudo-element blobs, leaves
   the element's own position/flow alone. Parent should be
   `position: relative; overflow: hidden` for correct clipping. */
.aurora {
  position: relative;
  isolation: isolate;
}
.aurora::before, .aurora::after {
  content: "";
  position: absolute;
  width: 55vw;
  height: 55vw;
  border-radius: 999px;
  filter: blur(60px);
  opacity: 0.45;
  pointer-events: none;
  z-index: -1;
}
.aurora::before {
  top: -15%; left: -10%;
  background: radial-gradient(closest-side, var(--primary), transparent);
}
.aurora::after {
  bottom: -25%; right: -10%;
  background: radial-gradient(closest-side, var(--accent-cyan), transparent);
}

/* Line clamp — cross-browser fallback (Tailwind's utility sometimes misses). */
.line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.line-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.line-clamp-4 { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }

/* Prose tweaks for CKEditor HTML on detail pages. */
.prose p      { margin: 1em 0; line-height: 1.75; color: var(--fg-muted); }
.prose h2     { font-size: 1.5rem; font-weight: 700; margin: 1.8em 0 0.5em; color: var(--fg); font-family: var(--font-display); }
.prose h3     { font-size: 1.25rem; font-weight: 600; margin: 1.5em 0 0.4em; color: var(--fg); font-family: var(--font-display); }
.prose a      { color: var(--primary); text-decoration: underline; text-underline-offset: 3px; }
.prose ul, .prose ol { margin: 1em 0 1em 1.5em; color: var(--fg-muted); }
.prose ul { list-style: disc; } .prose ol { list-style: decimal; }
.prose blockquote { border-left: 3px solid var(--primary-lo); padding-left: 1rem; color: var(--fg-subtle); font-style: italic; margin: 1.25em 0; }
.prose img { border-radius: var(--radius-sm); margin: 1em 0; }
.prose table { width: 100%; border-collapse: collapse; margin: 1.25em 0; }
.prose th, .prose td { border: 1px solid var(--border); padding: 0.5rem 0.75rem; text-align: left; }

/* Timeline dot (genealogy). */
.timeline-dot {
  width: 1.25rem; height: 1.25rem; border-radius: 999px;
  background: var(--bg-elevated);
  border: 2px solid var(--primary);
  display: grid; place-items: center;
}
.timeline-dot::after {
  content: ""; width: 0.5rem; height: 0.5rem; border-radius: 999px;
  background: var(--primary);
}

/* Theme toggle sun/moon transitions. */
.theme-toggle svg { transition: transform .3s ease, opacity .3s ease; }
html:not(.dark) .theme-toggle .icon-sun  { opacity: 0; transform: rotate(-90deg) scale(.6); position: absolute; }
html.dark        .theme-toggle .icon-moon { opacity: 0; transform: rotate(90deg)  scale(.6); position: absolute; }

/* Horizontal scroll shadow cue (for overflow rails). */
.scroll-rail { scrollbar-width: thin; }
.scroll-rail::-webkit-scrollbar { height: 8px; }
.scroll-rail::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; }

/* Count-up animation origin. */
[data-counter] { font-variant-numeric: tabular-nums; }

/* Hide x-cloak-like elements initially (kept for future). */
[x-cloak] { display: none !important; }

/* -------------------------------------------------------------
   MU theme presence + signature interactions
   ------------------------------------------------------------- */

/* Thin solid gold divider used between major sections. */
.mu-divider {
  height: 3px;
  background: var(--brand-gold);
  opacity: 0.9;
}

/* Cards get a stronger MU-blue hover border, complementing the generic lift. */
.card:hover,
.g-card:hover,
.tilt-card:hover {
  border-color: color-mix(in oklab, var(--primary) 45%, transparent) !important;
}

/* Tinted alternating sections (every other section) to break up the white. */
.tint-blue   { background: color-mix(in oklab, #1D428A 5%, var(--bg)); }
.tint-gold   { background: color-mix(in oklab, #F5B800 6%, var(--bg)); }

/* ---- 3D tilt card ---------------------------------------------
   Apply `.tilt-card` on any element; JS reads mouse position and
   writes `--rx`, `--ry` variables. Falls back gracefully if JS
   never fires (just stays flat). */
.tilt-card {
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transform-style: preserve-3d;
  transition: transform .3s cubic-bezier(.2,.8,.2,1), box-shadow .3s ease;
  will-change: transform;
}
/* Depth cue while tilting — one calm shadow, not a glow. */
.tilt-card:hover { box-shadow: 0 18px 36px -20px rgb(2 8 23 / 0.45); }
.tilt-card > .tilt-inner {
  transform: translateZ(20px);
  transition: transform .3s cubic-bezier(.2,.8,.2,1);
}

/* ---- Magnetic button ------------------------------------------
   JS translates the element toward the cursor when it's within
   the trigger radius; CSS just smooths the return. */
.btn-magnetic {
  transition: transform .25s cubic-bezier(.2,.8,.2,1), background-color .2s ease;
  will-change: transform;
}

/* ---- Hero cursor spotlight ------------------------------------
   A radial gradient that follows the cursor inside `.spotlight`.
   CSS custom properties `--mx`, `--my` are set by JS in % units. */
.spotlight { position: relative; isolation: isolate; }
.spotlight::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  background: radial-gradient(
    600px circle at var(--mx, 50%) var(--my, 50%),
    color-mix(in oklab, #F5B800 22%, transparent) 0%,
    transparent 60%);
  opacity: 0;
  transition: opacity .4s ease;
  z-index: -1;
}
.spotlight:hover::after { opacity: 1; }

/* ---- Underline reveal on major headings in-view ---------------
   Elements with `.h-underline` get an animated blue/gold underline
   that draws itself when scrolled into view via `.is-in`. */
.h-underline {
  display: inline-block;
  position: relative;
  padding-bottom: 0.35em;
}
.h-underline::after {
  content: "";
  position: absolute; left: 0; bottom: 0;
  height: 3px; width: 100%;
  background: var(--brand-gold);
  border-radius: 3px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .9s cubic-bezier(.2,.8,.2,1);
}
.h-underline.is-in::after,
.h-underline[data-reveal].is-in::after { transform: scaleX(1); }

/* Sliding footer ticker — pinned to the bottom of the viewport, content
   scrolls right→left continuously. `.marquee-track` is duplicated in
   markup so the wrap-around is seamless. Pauses on hover so a visitor
   can read a stat they spotted mid-scroll. */
.marquee-footer {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  background: var(--brand-blue-deep);
  color: var(--brand-gold);
  padding: 0.55rem 0;
  box-shadow: 0 -4px 20px -8px rgb(0 0 0 / 0.3);
  pointer-events: auto;
}
.marquee {
  overflow: hidden;
  white-space: nowrap;
  mask-image: linear-gradient(90deg, transparent, black 4%, black 96%, transparent);
}
.marquee-track {
  display: inline-flex;
  gap: 2.5rem;
  padding-left: 2.5rem;
  animation: marquee 45s linear infinite;
  will-change: transform;
}
.marquee-footer:hover .marquee-track,
.marquee-footer:focus-within .marquee-track,
.marquee-footer.is-paused .marquee-track { animation-play-state: paused; }
@keyframes marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
}
/* Pause/play control — WCAG 2.2.2: moving content needs a user stop. */
.marquee-pause {
  position: absolute;
  right: 0.5rem; top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: grid; place-items: center;
  width: 1.9rem; height: 1.9rem;
  border-radius: 999px;
  color: var(--brand-gold);
  background: rgb(255 255 255 / 0.08);
  border: 1px solid color-mix(in oklab, var(--brand-gold) 40%, transparent);
  cursor: pointer;
}
.marquee-pause:hover { background: rgb(255 255 255 / 0.16); }
.marquee-pause .icon-play,
.marquee-footer.is-paused .marquee-pause .icon-pause { display: none; }
.marquee-footer.is-paused .marquee-pause .icon-play { display: block; }
/* Reserve footer space only when the ticker exists; hide it on small
   screens where 42px of fixed chrome costs too much viewport. */
body:has(.marquee-footer) { padding-bottom: 2.75rem; }
@media (max-width: 640px) {
  .marquee-footer { display: none; }
  body:has(.marquee-footer) { padding-bottom: 0; }
}


/* ============================================================================
   EDITORIAL SYSTEM (HAI-inspired)
   — components built on top of the tokens above. Templates SHOULD use these
   classes rather than re-implementing the markup inline.
   ============================================================================ */

/* -- Global focus ring (accessibility) ---------------------------------- */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}

/* -- Containers (content widths) --------------------------------------- */
.container-narrow   { max-width: var(--container-narrow);   margin-left: auto; margin-right: auto; padding-left: var(--space-md); padding-right: var(--space-md); }
.container-standard { max-width: var(--container-standard); margin-left: auto; margin-right: auto; padding-left: var(--space-md); padding-right: var(--space-md); }
.container-wide     { max-width: var(--container-wide);     margin-left: auto; margin-right: auto; padding-left: var(--space-md); padding-right: var(--space-md); }

/* -- Kicker — small uppercase eyebrow with colored leading dot.
   Consumers: .kicker with optional `.kicker--news|research|grants|people`.    */
.kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--brand-blue);
}
.kicker::before {
  content: "";
  width: 0.4rem; height: 0.4rem;
  border-radius: var(--radius-pill);
  background: currentColor;
}
.kicker--news     { color: var(--brand-blue); }
.kicker--research { color: var(--accent-cyan); }
.kicker--grants   { color: var(--accent-rose); }
.kicker--people   { color: var(--accent-violet); }
.kicker--featured { color: var(--brand-gold-deep); }
.dark .kicker--featured { color: var(--brand-gold); }

/* -- Topic pill — role-coloured tag used under cards.
   Override the colour via `style="--role: var(--accent-cyan)"` or a modifier. */
.topic-pill {
  --role: var(--brand-blue);
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.2rem 0.7rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  background:   color-mix(in oklab, var(--role) 12%, transparent);
  color:        var(--role);
  border: 1px solid color-mix(in oklab, var(--role) 25%, transparent);
  transition: background-color var(--duration-fast) var(--ease-out);
}
.topic-pill--featured { --role: var(--brand-gold); color: var(--brand-gold-deep); }
.topic-pill--muted    {
  --role: var(--fg-subtle);
  background: var(--surface-2);
  color: var(--fg-muted);
  border-color: var(--border);
}
.topic-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  align-items: center;
}

/* -- Byline — author list used on publications & news detail. ---------- */
.byline {
  font-size: var(--fs-caption);
  color: var(--fg-muted);
  line-height: var(--lh-body);
}
.byline strong { color: var(--fg); font-weight: 600; }

/* -- Page hero — solid Marquette navy band above main content on every
   page: white display title, gold kicker + rule, 3px gold seam at the
   bottom edge. Same treatment as the home CTA and about/contact bands,
   so every "heading part" across the site matches. Legacy tint modifiers
   (--rose/--emerald/--violet) are no-ops now — one brand, one band.       */
.page-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-top: clamp(3rem, 5vw, 5rem);
  padding-bottom: clamp(2.5rem, 4vw, 3.5rem);
  background: var(--brand-blue-deep);
}
.page-hero::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: var(--brand-gold);
  pointer-events: none;
  z-index: 1;
}
/* On-navy element treatments — beat the per-page colour variants. */
.page-hero .kicker { color: var(--brand-gold); }
.page-hero .chip,
.page-hero .stat-chip {
  background: rgb(255 255 255 / 0.09);
  color: rgb(255 255 255 / 0.88);
  border-color: rgb(255 255 255 / 0.24);
}
.page-hero .stat-chip__num   { color: var(--brand-gold); }
.page-hero .stat-chip__label { color: rgb(255 255 255 / 0.8); }
.page-hero .btn-ghost {
  color: #fff;
  border-color: rgb(255 255 255 / 0.35);
}
.page-hero .btn-ghost:hover {
  background: rgb(255 255 255 / 0.12);
  border-color: rgb(255 255 255 / 0.6);
}
.page-hero .topic-pill {
  color: rgb(255 255 255 / 0.88);
  background: rgb(255 255 255 / 0.09);
  border-color: rgb(255 255 255 / 0.24);
}
.page-hero .topic-pill--featured {
  color: var(--brand-gold);
  background: color-mix(in oklab, var(--brand-gold) 14%, transparent);
  border-color: color-mix(in oklab, var(--brand-gold) 45%, transparent);
}

.page-hero__title {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
  color: #fff;
}
.page-hero__lede {
  margin-top: var(--space-sm);
  max-width: 40rem;
  font-size: 1.125rem;
  line-height: var(--lh-body);
  color: rgb(255 255 255 / 0.78);
}
.page-hero__lede a,
.page-hero__lede strong { color: #fff; }

/* -- Section header — kicker + h2 + optional lede, reusable per section. -- */
.section-header {
  margin-bottom: var(--space-lg);
}
.section-header__title {
  margin-top: var(--space-2xs);
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  font-weight: 600;
  line-height: var(--lh-tight);
  color: var(--fg);
}
.section-header__lede {
  margin-top: var(--space-xs);
  max-width: 40rem;
  color: var(--fg-muted);
}

/* -- Filter bar — sticky search/filter strip. --------------------------- */
.filter-bar {
  position: sticky;
  top: 4rem;  /* below main nav */
  z-index: 20;
  background: color-mix(in srgb, var(--bg-elevated) 82%, transparent);
  backdrop-filter: saturate(1.2) blur(14px);
  -webkit-backdrop-filter: saturate(1.2) blur(14px);
  border-top:    1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.filter-bar__inner {
  padding-top: var(--space-xs);
  padding-bottom: var(--space-xs);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: center;
}

/* -- Figure panel — media aspect-ratio wrapper. ------------------------- */
.figure-panel {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  aspect-ratio: var(--ratio, 16 / 11);
  background: var(--surface-2);
}
.figure-panel > img,
.figure-panel > video {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}
.figure-panel--portrait { --ratio: 4 / 5; }
.figure-panel--square   { --ratio: 1 / 1; }
.figure-panel--wide     { --ratio: 16 / 9; }

/* -- Pub plate — solid "JOURNAL · 2025" panel used wherever content has no
   image. One flat role colour (anchored toward Marquette deep navy), a
   flat gold baseline bar, and an optional brand-artwork layer — no
   gradients, no weave, no glow.                                            */
.pub-plate {
  --role: var(--brand-blue);
  /* Flat computed colour: role hue pulled toward deep navy so every plate
     sits in the same Marquette family in both themes. */
  --plate-bg: color-mix(in oklab, var(--role) 78%, var(--brand-blue-deep));
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 16 / 11;
  background: var(--plate-bg);
  color: white;
}
html.dark .pub-plate {
  /* Accents lift in dark mode; re-anchor toward navy so white type keeps
     its contrast and the panel stays deep + solid. */
  --plate-bg: color-mix(in oklab, var(--role) 45%, var(--brand-blue-deep));
}
/* Structural layer (same markup as before): flat gold baseline bar. */
.pub-plate__weave {
  position: absolute; inset: 0;
  pointer-events: none;
  background: linear-gradient(var(--brand-gold), var(--brand-gold))
              left bottom / 100% 4px no-repeat;
}
/* Brand artwork variants — flat vector art (static/img/defaults/) drawn on
   the same solid navy field as the plates, so the panel still reads as one
   committed colour. Applied per content type, e.g. `pub-plate pub-plate--projects`.
   The plain --plate-bg stays underneath as the pre-load fallback. */
.pub-plate--publications, .pub-plate--projects,
.pub-plate--news,         .pub-plate--facilities,
.pub-plate--grants {
  background-image: var(--plate-art);
  background-size: cover;
  background-position: center;
}
.pub-plate--publications { --plate-art: url("../img/defaults/web/publications.jpg"); }
.pub-plate--projects     { --plate-art: url("../img/defaults/web/projects.jpg"); }
.pub-plate--news         { --plate-art: url("../img/defaults/web/news.jpg"); }
.pub-plate--grants       { --plate-art: url("../img/defaults/web/grants.jpg"); }
/* Facilities keep the generic SVG as the class default; each row on
   facilities.html overrides --plate-art inline with its own photo. */
.pub-plate--facilities   { --plate-art: url("../img/defaults/facilities.svg"); }
/* On artwork plates the centered label sits on a solid navy chip, museum-
   label style, so type never fights the illustration behind it. */
.pub-plate--publications .pub-plate__content > div,
.pub-plate--projects     .pub-plate__content > div,
.pub-plate--news         .pub-plate__content > div,
.pub-plate--facilities   .pub-plate__content > div,
.pub-plate--grants       .pub-plate__content > div {
  background: var(--brand-blue-deep);
  padding: 0.5rem 0.95rem 0.6rem;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in oklab, var(--brand-gold) 35%, transparent);
}
.pub-plate__content {
  position: absolute; inset: 0;
  display: grid;
  place-items: center;
  padding: var(--space-sm);
  text-align: center;
  font-family: var(--font-display);
}
.pub-plate__kicker {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--brand-gold);
}
.pub-plate__year {
  margin-top: var(--space-2xs);
  font-size: clamp(2rem, 3vw, 3rem);
  line-height: 1;
  font-weight: 600;
  font-variant-numeric: lining-nums tabular-nums;
  text-shadow: 0 1px 2px rgb(2 8 23 / 0.35);
}
/* Name line for plates whose subject is a title, not a year (facilities,
   projects) — a big lone initial read as incomplete. */
.pub-plate__name {
  margin-top: var(--space-2xs);
  font-weight: 600;
  font-size: 1.05rem;
  line-height: 1.35;
  max-width: 18ch;
  color: #fff;
}
.pub-plate__typebadge {
  position: absolute;
  top: var(--space-xs); left: var(--space-xs);
  background: rgba(255, 255, 255, 0.95);
  color: var(--brand-blue-deep);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.25rem var(--space-2xs);
  border-radius: var(--radius-pill);
}

/* -- List divider — editorial stack rhythm. ----------------------------- */
.list-divider > li + li {
  border-top: 1px solid var(--border);
}

/* -- Editorial row — HAI's primary story / paper card.
   Variants: base, --compact (narrower image), --featured (more prominent).  */
.editorial-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
  padding: var(--space-md) 0;
  transition: background-color var(--duration-fast) var(--ease-out);
}
@media (min-width: 768px) {
  .editorial-row            { grid-template-columns: 240px 1fr; gap: var(--space-lg); padding: var(--space-lg) 0; }
  .editorial-row--compact   { grid-template-columns: 160px 1fr; gap: var(--space-md); padding: var(--space-md) 0; }
  .editorial-row--featured  { grid-template-columns: 2fr 3fr;   gap: var(--space-xl); padding: var(--space-xl) 0; }
}
.editorial-row:hover .figure-panel > img { transform: scale(1.03); }
/* Stretch the figure panel inside an editorial row to match the body
   column height. Aspect-ratio kicks in only as a minimum (min-height)
   so the plate still has a usable size when the body content is short. */
@media (min-width: 768px) {
  .editorial-row { align-items: stretch; }
  .editorial-row > .figure-panel,
  .editorial-row > div > .figure-panel {
    aspect-ratio: auto;
    height: 100%;
    min-height: calc(240px * 11 / 16);
    align-self: stretch;
  }
  .editorial-row .pub-plate { aspect-ratio: auto; height: 100%; min-height: 100%; }
}
.editorial-row__kicker-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  font-size: var(--fs-eyebrow);
  color: var(--fg-subtle);
  margin-bottom: var(--space-2xs);
}
.editorial-row__title {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 600;
  line-height: var(--lh-tight);
  color: var(--fg);
  letter-spacing: -0.01em;
}
.editorial-row__title a {
  transition: color var(--duration-fast) var(--ease-out);
}
.editorial-row:hover .editorial-row__title a { color: var(--brand-blue); }
.dark .editorial-row:hover .editorial-row__title a { color: var(--primary); }
.editorial-row__excerpt {
  margin-top: var(--space-xs);
  color: var(--fg-muted);
  line-height: var(--lh-body);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.editorial-row__footer {
  margin-top: var(--space-md);
  padding-top: var(--space-sm);
  border-top: 1px dashed var(--border);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-xs);
}

/* -- Card variants —
   `.card--flat` = border-only, no shadow (HAI reading view).
   `.card` (existing) already defined above.                               */
.card--flat {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  transition: border-color var(--duration-fast) var(--ease-out);
}
.card--flat:hover { border-color: var(--border-strong); }

/* -- Editorial pagination. --------------------------------------------- */
.pagination--editorial {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  margin-top: var(--space-xl);
  font-size: var(--fs-caption);
}
.pagination--editorial a {
  padding: var(--space-2xs) var(--space-sm);
  border-radius: var(--radius-sm);
  color: var(--fg);
  transition: background-color var(--duration-fast) var(--ease-out);
}
.pagination--editorial a:hover { background: var(--surface-2); }

/* -- Deprecated chip variants (kept for one release — prefer .topic-pill).
   Semantic alias: chip-indigo → chip-brand; chip-amber → chip-featured.    */
.chip-brand     { background: color-mix(in oklab, var(--brand-blue) 12%, transparent); color: var(--brand-blue); border-color: color-mix(in oklab, var(--brand-blue) 30%, transparent); }
.chip-featured  { background: color-mix(in oklab, var(--brand-gold) 15%, transparent); color: var(--brand-gold-deep); border-color: color-mix(in oklab, var(--brand-gold) 35%, transparent); }
.dark .chip-featured { color: var(--brand-gold); }

/* ============================================================================
   POLISH PASSES A–D  (appended 2026-04-23)
   Improvements on top of existing tokens. Non-destructive — overrides by
   specificity only. Safe to remove as a block to revert.
   ============================================================================ */

/* ---------- Pass A: Hero refinement -------------------------------------- */

/* 1. Tighten hero vertical rhythm — the old pb was causing 200–300px of dead
   space between the stat chips and the first content row. Keep enough breath. */
.page-hero {
  padding-top: clamp(2.5rem, 4vw, 4rem);
  padding-bottom: clamp(1.75rem, 3vw, 2.5rem);
}

/* 2. Right-side anchor — tasteful gold-on-navy diagonal weave, fully decorative.
   Sits behind the text, occupies the empty right half of every listing-page
   hero. Disabled on mobile to avoid noise. Respects reduced-motion by
   default (no movement, just a static pattern). */
.page-hero::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: min(45%, 640px);
  pointer-events: none;
  z-index: -1;
  /* Crisp diagonal pinstripes only — white hairlines on the navy band. */
  background-image:
    repeating-linear-gradient(135deg,
      rgb(255 255 255 / 0.06) 0,
      rgb(255 255 255 / 0.06) 1px,
      transparent 1px, transparent 22px);
  opacity: 0.9;
  mask-image: linear-gradient(270deg, black 0%, black 60%, transparent 100%);
  -webkit-mask-image: linear-gradient(270deg, black 0%, black 60%, transparent 100%);
}
@media (max-width: 768px) {
  .page-hero::after { display: none; }
}

/* 3. Hero title — tighten tracking, crisp fallback shadow under dark mode. */
.page-hero__title {
  font-weight: 600;
  letter-spacing: -0.02em;
}
.dark .page-hero__title {
  text-shadow: 0 1px 24px rgba(0, 0, 0, 0.35);
}

/* 4. Home hero kicker — align "Our Home" to the eyebrow convention used on
   every other page. Only affects the topic-pill--featured pill inside the
   home carousel; other pages are untouched. */
[data-carousel] .topic-pill--featured {
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  padding: 0.3rem 0.75rem 0.3rem 0.9rem;
  position: relative;
}
[data-carousel] .topic-pill--featured::before {
  content: "";
  width: 0.4rem; height: 0.4rem;
  border-radius: 999px;
  background: currentColor;
  margin-right: 0.25rem;
  display: inline-block;
  vertical-align: middle;
}

/* 5. Carousel counter restyling — make the "1 / 3" pair feel editorial:
   larger current number in display serif, muted separator + total.
   Pure CSS on existing markup, no JS changes needed. */
[data-carousel-counter] {
  font-family: var(--font-numeric);
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--fg);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* ---------- Pass B: Unified chip / stat-chip system ---------------------- */

/* Baseline tightening — existing `.chip` and `.topic-pill` sizes diverged.
   Re-align both to one size, keep color variants intact. */
.chip,
.topic-pill {
  padding: 0.3rem 0.75rem;
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
}

/* Stat chip — larger, role-coded, designed for the hero "79 members · 51 papers"
   row. Numbers become the focal point. */
.stat-chip {
  --role: var(--primary);
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.4rem 0.85rem;
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--role) 10%, transparent);
  border: 1px solid color-mix(in oklab, var(--role) 25%, transparent);
  color: var(--role);
  line-height: 1;
}
.stat-chip__num {
  font-family: var(--font-numeric);
  font-weight: 700;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.stat-chip__label {
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: 0.04em;
  color: color-mix(in oklab, var(--role) 85%, var(--fg));
}
.dark .stat-chip__label { color: color-mix(in oklab, var(--role) 60%, var(--fg)); }

/* Backfill: existing ".chip" rows in hero contexts pick up tabular nums and
   a subtle hover raise, so they feel of-a-piece with .stat-chip. */
.page-hero .chip { transition: transform var(--duration-fast) var(--ease-out); }
.page-hero .chip:hover { transform: translateY(-1px); }

/* ---------- Pass C: Card polish & avatar fallback ------------------------ */

/* Unified card hover — crisp lift + 1px role outline. No colour glow;
   works for .card, .tilt-card, .roster-card, .g-card without markup changes. */
.card, .tilt-card, .roster-card, .g-card {
  --role: var(--primary);
}
.card:hover,
.tilt-card:hover,
.roster-card:hover,
.g-card:hover {
  box-shadow:
    var(--shadow),
    0 0 0 1px color-mix(in oklab, var(--role) 35%, transparent);
}

/* Canonical avatar fallback — solid role-tinted navy, white serif initial,
   thin inset gold ring. Any template using `.avatar-fallback` or
   `.roster-card__fallback` picks this up; override with inline
   `style="--role: var(--accent-cyan)"` if a section wants its own hue. */
.avatar-fallback,
.roster-card__fallback {
  background: color-mix(in oklab, var(--role, var(--brand-blue)) 60%, var(--brand-blue-deep)) !important;
  color: #fff !important;
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.02em;
  position: relative;
}
.avatar-fallback::after,
.roster-card__fallback::after {
  content: "";
  position: absolute;
  inset: 3px;
  border: 1px solid color-mix(in oklab, var(--brand-gold) 55%, transparent);
  border-radius: inherit;
  pointer-events: none;
}

/* ---------- Pass D: Mobile, dark mode, a11y ------------------------------ */

/* Mobile brand strip — condense to a single line of essentials so it stops
   truncating at 390px. */
@media (max-width: 640px) {
  .uni-strip .max-w-\[1440px\] {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
  }
  .uni-strip .max-w-\[1440px\] span:first-child {
    font-size: 10px;
    letter-spacing: 0.14em;
  }
}

/* Dark mode hero button contrast — btn-ghost was nearly invisible against
   the dark gradient. Lift border + add subtle fill so it reads without
   losing the ghost affordance. */
.dark .page-hero .btn-ghost,
.dark [data-carousel] .btn-ghost {
  border-color: color-mix(in oklab, var(--fg) 30%, transparent);
  background: color-mix(in oklab, var(--fg) 4%, transparent);
  color: var(--fg);
}
.dark .page-hero .btn-ghost:hover,
.dark [data-carousel] .btn-ghost:hover {
  background: color-mix(in oklab, var(--fg) 10%, transparent);
  border-color: color-mix(in oklab, var(--fg) 50%, transparent);
}

/* Section-heading rule under page-hero titles — gold accent bar, 40×2px,
   reinforces the eyebrow → title → rule pattern used on brand-forward sites.
   Also available as a standalone `.hero-rule` helper for detail-page H1s
   that can't use the `.page-hero__title` class (e.g. white-on-gradient
   titles where enforcing `color: var(--fg)` would break them). */
.page-hero__title::after,
.hero-rule::after {
  content: "";
  display: block;
  width: 3rem;
  height: 3px;
  margin-top: 1rem;
  border-radius: 3px;
  background: var(--brand-gold);
  opacity: 0.9;
}
.hero-rule--on-dark::after {
  background: var(--brand-gold);
  opacity: 0.9;
}

/* Reduced motion — respect user preference globally. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .tilt-card { transform: none !important; }
  .btn-magnetic { transform: none !important; }
  [data-reveal].js-reveal { opacity: 1 !important; transform: none !important; }
  .marquee-track { animation: none !important; }
  [data-carousel]::before { transition: none !important; }
}

/* Print — hide decorative backdrops / interactive affordances for a clean
   research-page printout. */
@media print {
  .page-hero::before,
  .page-hero::after,
  .aurora::before,
  .aurora::after,
  .spotlight::after,
  .marquee,
  [data-carousel-prev],
  [data-carousel-next] {
    display: none !important;
  }
  body { background: white !important; }
  .page-hero__title { color: #000 !important; }
}

/* Focus ring on the theme toggle — the <button> inherits global focus ring
   but the sun/moon absolute positioning hides the outline. Re-assert. */
.theme-toggle:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 3px;
  border-radius: 999px;
}

/* Skip link — keyboard a11y. Hidden off-screen until focused. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0.5rem;
  z-index: 10000;
  padding: 0.5rem 1rem;
  background: var(--brand-blue-deep);
  color: #fff;
  border-radius: var(--radius-sm);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { left: 1rem; }

/* ============================================================================
   MOTION FOUNDATION  (GSAP + ScrollTrigger + Lenis)  — appended 2026-07-02
   Pairs with static/js/animations.js. Everything here degrades safely:
   the pre-hide only applies while the class `anim-armed` is present (added by
   JS, so no-JS never hides content) and is force-cleared by `reveal-all`.
   ============================================================================ */

/* -- Reveal safety net --------------------------------------------------- */
html.anim-armed [data-reveal],
html.anim-armed [data-hero-item],
html.anim-armed [data-hero-media],
html.anim-armed [data-split] { opacity: 0; }

html.reveal-all [data-reveal],
html.reveal-all [data-hero-item],
html.reveal-all [data-hero-media],
html.reveal-all [data-split],
html.reveal-all [data-split] .char {
  opacity: 1 !important;
  visibility: visible !important;
  transform: none !important;
}
/* The failsafe MUST also un-clip the reveal mask — onComplete is not guaranteed
   to fire (thrown error, 6s timeout, no-GSAP, reduced-motion). Without this a
   broken/aborted timeline leaves .word clipped forever and the italic "Lab" b
   (which overhangs to the right) plus tall glyphs stay sheared. */
html.reveal-all [data-split] .word { overflow: visible !important; }
html.reveal-all .g-snake { clip-path: none !important; }

@media (prefers-reduced-motion: reduce) {
  html.anim-armed [data-reveal],
  html.anim-armed [data-hero-item],
  html.anim-armed [data-hero-media] { opacity: 1; }
}

/* -- Lenis smooth scroll (library-required rules) ------------------------ */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }
.lenis.lenis-smooth iframe { pointer-events: none; }

/* -- Scroll progress bar ------------------------------------------------- */
.scroll-progress {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 3px;
  z-index: 100;
  pointer-events: none;
  background: color-mix(in oklab, var(--brand-blue) 12%, transparent);
}
.scroll-progress > span {
  display: block;
  height: 100%;
  width: 100%;
  transform: scaleX(0);
  transform-origin: left center;
  background: var(--brand-gold);
}

/* -- Nav scroll states (refined by animations.js) ------------------------ */
[data-scroll-nav] {
  transition: transform .45s var(--ease-out), box-shadow .3s ease,
              background-color .3s ease, height .3s ease;
  will-change: transform;
}
[data-scroll-nav].nav-scrolled {
  box-shadow: 0 8px 30px -18px color-mix(in oklab, var(--brand-blue-deep) 80%, transparent);
  background: color-mix(in oklab, var(--bg-elevated) 96%, transparent) !important;
}
/* R8 — nav stays put: hide-on-scroll disabled; keep sticky + always visible. */
[data-scroll-nav].nav-hidden { transform: none; }

/* -- Nav links: one consistent, high-clarity hover/active treatment --------
   Every top-nav item (links + the Members dropdown) shares this. In dark mode
   the gold accent text always sits on a NEUTRAL tint (never a gold-on-gold
   wash), so it stays crisp. Replaces the ad-hoc per-link Tailwind hover
   utilities that made the dropdown look different from the plain links. */
.nav-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.8rem;
  border-radius: 0.55rem;
  color: var(--fg);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  transition: color 0.2s var(--ease-out), background-color 0.2s var(--ease-out);
}
.nav-link:hover {
  color: var(--brand-blue);
  background: color-mix(in oklab, var(--brand-blue) 9%, transparent);
}
.nav-link.is-active {
  color: var(--brand-blue);
  background: color-mix(in oklab, var(--brand-blue) 14%, transparent);
  font-weight: 600;
}
html.dark .nav-link:hover {
  color: var(--brand-gold);
  background: color-mix(in oklab, var(--fg) 9%, transparent);
}
html.dark .nav-link.is-active {
  color: var(--brand-gold);
  background: color-mix(in oklab, var(--fg) 15%, transparent);
  font-weight: 600;
}

/* Dropdown menu items (Current / Alumni) — same neutral-tint clarity rule. */
.nav-menu-item {
  display: block;
  padding: 0.55rem 0.75rem;
  border-radius: 0.4rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--fg);
  transition: color 0.2s var(--ease-out), background-color 0.2s var(--ease-out);
}
.nav-menu-item:hover,
.nav-menu-item:focus {
  color: var(--brand-blue);
  background: var(--surface-2);
  outline: none;
}
html.dark .nav-menu-item:hover,
html.dark .nav-menu-item:focus {
  color: var(--brand-gold);
  background: color-mix(in oklab, var(--fg) 10%, transparent);
}

/* -- Split text (hero heading) ------------------------------------------- */
[data-split] .word {
  display: inline-block;
  /* The reveal mask must clear the glyphs on BOTH axes. The reveal is bottom-up
     (chars slide yPercent 118 -> 0), so vertical padding gives the transient
     slide its headroom (padding-top for ascenders, padding-bottom for Ubicomp's
     p descender). BUT the italic gold "Lab" b overhangs HORIZONTALLY — its serif
     italic slant pushes the ink ~6px past the box's right edge — which vertical
     padding cannot fix. `overflow: clip` + `overflow-clip-margin` lets glyphs
     lean/rise slightly outside the box instead of being sheared, clearing the
     italic right overhang AND the ascender top on the normal animated path (not
     just the failsafe). margin-top cancels padding-top so the wordmark's resting
     position is unchanged. Degrades to plain clip where unsupported; JS/CSS then
     un-clip on completion. */
  overflow: clip;
  overflow-clip-margin: 0.35em;
  padding-top: 0.35em;
  padding-bottom: 0.18em;
  margin-top: -0.35em;
  vertical-align: top;
}
[data-split] .char {
  display: inline-block;
  will-change: transform;
}

/* -- Reduced-motion: neutralise the new decorative layers ---------------- */
@media (prefers-reduced-motion: reduce) {
  .scroll-progress { display: none; }
}

/* ============================================================================
   HOME COMPONENTS  — appended 2026-07-02
   ============================================================================ */

/* Hero wordmark accent — the gold italic "Lab". */
.hero-lab { color: var(--brand-gold); font-style: italic; }

/* Carousel dot indicators — active dot elongates into a pill. */
.carousel-dot {
  width: 0.5rem; height: 0.5rem; border-radius: 999px;
  background: color-mix(in oklab, var(--fg) 22%, transparent);
  border: none; padding: 0; cursor: pointer;
  transition: width .35s var(--ease-out), background-color .35s var(--ease-out);
}
.carousel-dot:hover { background: color-mix(in oklab, var(--brand-blue) 55%, transparent); }
.carousel-dot[aria-current="true"] {
  width: 1.5rem;
  background: var(--brand-gold);
}

/* Carousel arrows. */
.carousel-arrow {
  width: 2.5rem; height: 2.5rem; border-radius: 999px;
  display: grid; place-items: center;
  background: var(--bg-elevated); color: var(--fg);
  border: 1px solid var(--border); box-shadow: var(--shadow-sm);
  transition: transform .25s var(--ease-out), background-color .2s ease, border-color .2s ease;
}
.carousel-arrow:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklab, var(--brand-blue) 45%, var(--border));
}
.carousel-arrow--solid { background: var(--primary); color: #fff; border-color: var(--primary); }
.carousel-arrow--solid:hover { background: var(--primary-hi); border-color: var(--primary-hi); }

/* Featured news card — solid gold-tinted surface, full border, crisp lift.
   One flat colour, no sheen sweep. */
.news-card {
  display: flex; height: 100%;
  padding: 1.4rem 1.5rem;
  border-radius: var(--radius-lg);
  background: color-mix(in oklab, var(--brand-gold) 7%, var(--bg-elevated));
  border: 1px solid color-mix(in oklab, var(--brand-gold) 40%, var(--border));
  position: relative; overflow: hidden;
  transition: transform .3s var(--ease-out), border-color .3s ease;
}
.news-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklab, var(--brand-gold) 65%, var(--border));
}
/* Grid items default to min-width:auto, so a long unbreakable URL in a
   news excerpt can widen the whole page on mobile. Let cards shrink and
   wrap long tokens anywhere. */
main .grid > li,
main .grid > div { min-width: 0; }
.news-card p { overflow-wrap: anywhere; }
.news-card__more {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: var(--fs-caption); font-weight: 600;
  color: var(--brand-gold-deep);
}
.dark .news-card__more { color: var(--brand-gold); }
.news-card__more svg { transition: transform .2s var(--ease-out); }
.news-card:hover .news-card__more svg { transform: translateX(4px); }

/* ---- Hero photo showcase (large, clickable) --------------------------- */
.hero-photo { position: relative; }
/* .hero-photo__glow retired — the full-bleed hero hides it and the blurred
   backdrop fought the solid-colour design language. Markup keeps the empty
   span for backwards compatibility. */
.hero-photo__glow { display: none; }
.hero-photo__frame {
  position: relative; z-index: 1;
  display: block; width: 100%; padding: 0;
  aspect-ratio: 4 / 3;
  border-radius: 1.5rem; overflow: hidden;
  border: 1px solid var(--border);
  background: var(--surface-2);
  cursor: zoom-in;
  box-shadow: 0 34px 80px -34px color-mix(in oklab, var(--brand-blue-deep) 72%, transparent);
}
@media (min-width: 1024px) {
  .hero-photo__frame { aspect-ratio: auto; height: clamp(24rem, 40vw, 38rem); }
}
.hero-photo__slide img { width: 100%; height: 100%; object-fit: cover; }
.hero-photo__frame:hover .hero-photo__slide img { transform: scale(1.03); transition: transform .6s var(--ease-out); }
.hero-photo__fallback {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--font-display); font-size: 4rem; color: #fff;
  background: var(--brand-blue-deep);
}
.hero-photo__count {
  position: absolute; top: .85rem; left: .85rem; z-index: 3;
  padding: .25rem .6rem; border-radius: 999px;
  background: color-mix(in oklab, var(--brand-blue-deep) 60%, transparent);
  color: #fff; font-size: .72rem; font-weight: 600; letter-spacing: .03em;
  font-variant-numeric: tabular-nums; backdrop-filter: blur(6px);
}
.hero-photo__hint {
  position: absolute; bottom: .85rem; right: .85rem; z-index: 3;
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .35rem .7rem; border-radius: 999px;
  background: color-mix(in oklab, var(--brand-blue-deep) 60%, transparent);
  color: #fff; font-size: .72rem; font-weight: 600;
  backdrop-filter: blur(6px);
  opacity: 0; transform: translateY(6px);
  transition: opacity .3s var(--ease-out), transform .3s var(--ease-out);
}
.hero-photo__hint svg { width: .9rem; height: .9rem; }
.hero-photo__frame:hover .hero-photo__hint,
.hero-photo__frame:focus-visible .hero-photo__hint { opacity: 1; transform: translateY(0); }

/* Thumbnail rail below the hero photo. */
.hero-rail { display: flex; align-items: center; gap: .6rem; margin-top: 1rem; }
.hero-thumbs { display: flex; gap: .5rem; overflow-x: auto; flex: 1; padding: .15rem .1rem .4rem; scroll-behavior: smooth; }
.hero-thumb {
  flex: 0 0 auto; width: 4.4rem; height: 3.2rem;
  border-radius: .6rem; overflow: hidden; padding: 0; cursor: pointer;
  border: 2px solid transparent; background: var(--surface-2);
  opacity: .55;
  transition: opacity .25s ease, border-color .25s ease, transform .25s var(--ease-out);
}
.hero-thumb img { width: 100%; height: 100%; object-fit: cover; }
.hero-thumb:hover { opacity: 1; transform: translateY(-2px); }
.hero-thumb[aria-current="true"] {
  opacity: 1;
  border-color: var(--brand-gold);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--brand-blue) 40%, transparent);
}
.hero-thumb__ph { display: grid; place-items: center; width: 100%; height: 100%;
  font-family: var(--font-display); color: #fff;
  background: var(--brand-blue-deep); }

/* ---- Lightbox (full-screen photo viewer) ------------------------------ */
.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
  background: color-mix(in oklab, var(--brand-blue-deep) 84%, black 12%);
  opacity: 0; transition: opacity .3s ease;
}
.lightbox.is-open { opacity: 1; }
.lightbox[hidden] { display: none; }
html.lightbox-open { overflow: hidden; }
.lightbox__stage {
  margin: 0; display: flex; flex-direction: column; align-items: center; gap: 1rem;
  max-width: min(1200px, 92vw); max-height: 88vh;
  transform: scale(.96); transition: transform .3s var(--ease-out);
}
.lightbox.is-open .lightbox__stage { transform: scale(1); }
.lightbox__img {
  max-width: 100%; max-height: 78vh; width: auto; height: auto;
  object-fit: contain; border-radius: .75rem; background: #0b1220;
  box-shadow: 0 40px 90px -30px rgba(0,0,0,.7);
}
.lightbox__caption {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; justify-content: center;
  color: #fff; font-size: .95rem; text-align: center; max-width: 70ch;
}
.lightbox__counter { color: rgba(255,255,255,.6); font-size: .85rem; font-variant-numeric: tabular-nums; }
.lightbox__btn {
  position: absolute; z-index: 2; display: grid; place-items: center;
  width: 3rem; height: 3rem; border-radius: 999px; cursor: pointer;
  color: #fff; background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.25);
  backdrop-filter: blur(6px);
  transition: background-color .2s ease, transform .2s var(--ease-out);
}
.lightbox__btn[hidden] { display: none; }
.lightbox__btn:hover { background: rgba(255,255,255,.22); }
.lightbox__btn svg { width: 1.4rem; height: 1.4rem; }
.lightbox__close { top: clamp(1rem, 3vw, 1.75rem); right: clamp(1rem, 3vw, 1.75rem); }
.lightbox__prev { left: clamp(.5rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }
.lightbox__next { right: clamp(.5rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }
.lightbox__prev:hover { transform: translateY(-50%) scale(1.06); }
.lightbox__next:hover { transform: translateY(-50%) scale(1.06); }
@media (max-width: 640px) {
  .lightbox__prev, .lightbox__next { top: auto; bottom: 1rem; transform: none; }
  .lightbox__prev { left: 1rem; } .lightbox__next { right: 1rem; }
  .lightbox__prev:hover, .lightbox__next:hover { transform: scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
  .lightbox, .lightbox__stage { transition: none; }
}

/* Compact publication row — solid deep-cerulean block (publications role
   anchored toward navy), white type, slides right on hover. */
.pub-row {
  border-radius: var(--radius-sm);
  padding: 0.75rem 1rem;
  background: color-mix(in oklab, var(--accent-cyan) 55%, var(--brand-blue-deep));
  border: 1px solid color-mix(in oklab, var(--accent-cyan) 75%, var(--brand-blue-deep));
  transition: transform .25s var(--ease-out), border-color .25s ease;
}
html.dark .pub-row {
  /* Accents lift in dark mode; re-anchor toward navy to keep white type safe. */
  background: color-mix(in oklab, var(--accent-cyan) 32%, var(--brand-blue-deep));
  border-color: color-mix(in oklab, var(--accent-cyan) 50%, var(--brand-blue-deep));
}
.pub-row:hover {
  transform: translateX(5px);
  border-color: color-mix(in oklab, var(--brand-gold) 55%, var(--brand-blue-deep));
}

/* ===== PREMIUM CARD LAYER — retired 2026-07-03 =====
   The traveling beam ring + dual-tone glow hover was replaced by the solid
   Marquette card language: flat surfaces, crisp lift, 1px role outline
   (see .card / .content-card / .stat-card and the Pass C hover rule).
   Kept from that layer: the positioning scaffold components relied on, and
   the gentle plate/editorial lift. */
.content-card,
.stat-card,
.news-card,
.pub-row,
.person-tile,
.editorial-row,
.pub-plate,
.g-card,
.director-card,
.card {
  position: relative;
}
.pub-plate,
.editorial-row {
  transition:
    transform var(--duration-standard, 300ms) var(--ease-out, cubic-bezier(.2,.8,.2,1)),
    box-shadow var(--duration-standard, 300ms) var(--ease-out, cubic-bezier(.2,.8,.2,1));
}
.pub-plate:hover,
.editorial-row:hover {
  transform: translateY(-3px);
}
@media (prefers-reduced-motion: reduce) {
  .pub-plate:hover,
  .editorial-row:hover {
    transform: none;
  }
}

/* ===== ROUNDER + IMPROVED PLATE CARDS (appended) ===== */

/* R5 — Noticeably rounder corners on shared media panels + label plates.
   Both use var(--radius) as their single source of truth (lines 839, 858);
   we override to var(--radius-lg) here and round the inner media so the
   image visually matches the softened frame (overflow:hidden already clips,
   but border-radius:inherit protects sub-pixel edges + any future overflow). */
.figure-panel,
.pub-plate {
  border-radius: var(--radius-lg);
}
.figure-panel > img,
.figure-panel > video {
  border-radius: inherit;
}

/* R6 — Label-plate finish. The plate body itself is the flat solid colour
   defined on .pub-plate (plus optional flat brand artwork); this section
   keeps only the crisp finishing details: hairline gold ring, refined
   kicker with its gold dash, and the confident centered year. */

/* A gold hairline ring hugging the (rounder) plate edge for a framed feel.
   Non-interactive, behind content. */
.pub-plate::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--brand-gold) 26%, transparent);
}
/* Keep real content above the weave and the new ring. */
.pub-plate__content { z-index: 1; }

/* Crisper centered kicker — a little more presence + a subtle divider rule
   beneath, still centered. */
.pub-plate__kicker {
  position: relative;
  font-weight: 600;
  font-size: 0.6875rem;
  letter-spacing: 0.34em;
  text-indent: 0.34em; /* optical-center the tracked caps */
  color: color-mix(in oklab, var(--brand-gold) 92%, white);
  text-shadow: 0 1px 2px color-mix(in oklab, black 30%, transparent);
  padding-bottom: 0.5rem;
}
.pub-plate__kicker::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 1.75rem;
  height: 2px;
  transform: translateX(-50%);
  border-radius: var(--radius-pill);
  background: color-mix(in oklab, var(--brand-gold) 70%, transparent);
}

/* Big centered year / initial — tighter, more confident, gentle depth. */
.pub-plate__year {
  margin-top: 0.55rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: color-mix(in oklab, white 96%, var(--role));
  text-shadow: 0 2px 10px color-mix(in oklab, var(--brand-blue-deep) 55%, transparent);
}

/* Solid type badge — opaque white chip, deep-navy caps. */
.pub-plate__typebadge {
  background: #fff;
  color: var(--brand-blue-deep);
  font-weight: 700;
  letter-spacing: 0.12em;
  padding: 0.28rem 0.6rem;
  border: 1px solid color-mix(in oklab, var(--brand-blue) 12%, transparent);
}
html.dark .pub-plate__typebadge {
  background: var(--bg-elevated);
  color: color-mix(in oklab, var(--brand-gold) 90%, white);
  border-color: color-mix(in oklab, white 14%, transparent);
}


/* ============================================================================
   MARQUETTE.EDU GRADIENT PASS  (appended 2026-07-03)
   Cards and page-header bands move from flat navy to marquette.edu's 2024
   gradient language (tokens defined in Layer 1: --grad-gold, --grad-navy-band,
   --grad-navy-card, --grad-title). Flat solid rule stays for small chips and
   label plates; gradients are reserved for the band + card surfaces and the
   thin gold accents, exactly as marquette.edu uses them.
   ============================================================================ */

/* Page hero — cobalt→navy sweep with a faint cyan bloom in the lower-right,
   echoing marquette.edu's navy→cyan band sections. The Pass-A pinstripe
   ::after keeps its texture on top; the bottom seam becomes the gold
   gradient. */
.page-hero {
  background:
    radial-gradient(58rem 20rem at 88% 118%, rgb(0 205 255 / 0.15), transparent 68%),
    var(--grad-navy-band);
}
.page-hero::before { background: var(--grad-gold); }

/* Gold accent bars and rules pick up --gradient_gold_1. */
.page-hero__title::after,
.hero-rule::after,
.hero-rule--on-dark::after,
.stat-card::before,
.h-underline::after {
  background: var(--grad-gold);
}

/* Navy cards — same sweep as the band: cobalt light entering top-left,
   settling into primary navy. Borders/hover rules defined earlier still
   apply; only the surface changes. */
.stat-card,
.content-card {
  background: var(--grad-navy-card);
}

/* Pub-plate gold baseline becomes the gold gradient. */
.pub-plate__weave {
  background: var(--grad-gold) left bottom / 100% 4px no-repeat;
}

/* Featured news card — marquette.edu's signature gradient corner bracket
   (article_card-decor), scaled down. Sits under content, clipped by the
   card's rounded corner. */
.news-card::before {
  content: "";
  position: absolute;
  top: 0; left: 0;
  width: 4.75rem;
  height: 4.75rem;
  background: var(--grad-gold);
  clip-path: polygon(0 0, 100% 0, 100% 12%, 12% 12%, 12% 100%, 0 100%);
  pointer-events: none;
}

/* Section headers on light surfaces — marquette.edu's .h1 treatment:
   cyan kiss on the left, settling into navy (gradient-clipped text). */
.section-header__title {
  background-image: var(--grad-title);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* Dark theme: same idea, light end of the family so type stays bright. */
html.dark .section-header__title {
  background-image: linear-gradient(to right, #00CDFF 0%, #DEF2FC 32%, #F1F5F9 100%);
}
/* High-contrast preference: drop the clipped gradient, solid ink. */
@media (prefers-contrast: more) {
  .section-header__title {
    background-image: none;
    -webkit-text-fill-color: currentColor;
  }
}

/* Home director card is dark navy — render the icon-only profile links in
   white so they read on the gradient (the people.html hero is light and keeps
   the default treatment). */
.director-card .roster-card__link {
  color: #fff !important;
  background: rgb(255 255 255 / 0.14) !important;
}
.director-card .roster-card__link:hover {
  background: rgb(255 255 255 / 0.24) !important;
}

/* Profile-link icon row — shared base. Member tiles & alumni cards carry a
   near-identical inline copy; defining it here ensures pages WITHOUT that inline
   block (notably the home director card) still lay the icons out horizontally. */
.roster-card__links { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-top: auto; padding-top: 0.4rem; }
.roster-card__link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem;
  border-radius: var(--radius-sm);
  transition: transform var(--duration-fast) var(--ease-out);
}
.roster-card__link:hover { transform: translateY(-1px); }
/* Director links (people-page hero + home card) sit a little larger than the tiles. */
.director-hero .roster-card__link,
.director-card .roster-card__link { width: 2.25rem; height: 2.25rem; }
.director-hero .roster-card__link svg,
.director-card .roster-card__link svg { width: 1.15rem; height: 1.15rem; }
