/* ============================================
   CAIL Website Stylesheet
   Columbia-blue identity, grotesque display type,
   semantic tokens with automatic dark mode
   ============================================ */

@font-face {
    font-family: "Space Grotesk";
    /* Subset generated by scripts/subset_font.py from the full ...ttf.woff2
       (kept in git). Latin glyphs, weight axis 500-700 only. Regenerate with
       `uv run scripts/subset_font.py --src static/fonts/SpaceGroteskVariable.ttf.woff2
        --out static/fonts/SpaceGroteskVariable.subset.woff2 --wght-min 500 --wght-max 700`
       if the source font or ranges change. */
    src: url("../fonts/SpaceGroteskVariable.subset.woff2") format("woff2");
    font-weight: 500 700;
    font-style: normal;
    /* optional (not swap): the font is preloaded, so it almost always wins the
       ~100ms race and paints correctly on first render. On the rare cold-cache
       miss the page keeps the Georgia fallback for that view rather than
       swapping mid-paint -- avoids the wordmark/heading reflow flash. */
    font-display: optional;
}

/* Reset & Base */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

:root {
    color-scheme: light dark;

    /* Brand primitives - referenced only by the semantic tokens below */
    --columbia-blue: #0072CE;
    --navy: #1D4F91;
    --dark-blue: #003087;
    --light-blue: #B9D9EB;
    --pale-blue: #6CACE4;
    --red: #A6192E;
    /* Red legible on the navy hero/inverse bands in both modes */
    --red-bright: #FF8389;

    /* Semantic tokens - light */
    /* Faintly cool ground (lightest step of the blue-gray neutral family)
       so white --surface cards lift tonally, as they do in dark mode. */
    --bg: #FAFBFC;
    --bg-subtle: #F2F6FA;
    --bg-inverse: #0A2E5C;
    --surface: #FFFFFF;
    --text: #16232F;
    --text-muted: #46586A;
    --text-on-inverse: #FFFFFF;
    --text-on-inverse-muted: #C7DCF2;
    /* Near-ink, a half-step darker than --text so headings lead the
       hierarchy -- mirrors dark mode, where --heading sits a half-step
       brighter. Blue is reserved for interactive text; navy lives in
       surfaces (navbar, hero, footer), not type. */
    --heading: #101B26;
    --link: #0061B5;
    --link-hover: #003087;
    /* A step deeper than the #0072CE brand blue so accent-colored text
       clears 4.5:1 on white at any size, not just large. */
    --accent: #0066B8;
    --accent-contrast: #FFFFFF;
    --accent-red: var(--red);
    --nav-bg: var(--light-blue);
    --nav-text: var(--text);
    --nav-border: rgba(0, 48, 135, 0.2);
    --nav-logo-hover: var(--accent);
    --border: #D8E1EA;
    /* Hover state of a card hairline: one step deeper, still a hairline.
       Elevation language: in-flow surfaces (cards) are hairline + tonal
       step in BOTH modes; drop shadows are reserved for floating overlays
       (dropdown menus, hover cards, panels). */
    --border-strong: #AFBFCE;
    --selection-bg: var(--light-blue);
    --shadow-hover: 0 6px 20px rgba(10, 46, 92, 0.12);

    /* Typography */
    --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --font-heading: "Space Grotesk", 'Helvetica Neue', Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Menlo, Consolas, monospace;
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.375rem;
    --text-2xl: clamp(1.5rem, 1.2rem + 1.2vw, 1.875rem);
    --text-3xl: clamp(2rem, 1.5rem + 2vw, 2.75rem);
    /* Statement tier between the section titles and the hero display size:
       the homepage's lead section titles, so the page descends
       hero -> lead statements -> section titles instead of one flat size. */
    --text-4xl: clamp(2.4rem, 1.8rem + 2.4vw, 3.375rem);
    /* Large-type prose for the mission statement (display face at body scale). */
    --text-statement: clamp(1.375rem, 1.05rem + 1.3vw, 1.875rem);
    --text-display: clamp(3rem, 1.8rem + 5vw, 5.75rem);

    --container-max-width: 1200px;
    --border-radius: 4px;
    /* The one motion spec: every hover/fade on the site runs at this
       duration/easing (fast out, slow in). Bigger moves may not exist --
       if one ever does, derive it from this pair rather than inventing
       a new timing. */
    --transition: 0.18s cubic-bezier(0.2, 0, 0.2, 1);

    /* Desaturated SVG turbulence tile, layered on top of the hero / page-header
       gradients to kill banding on large displays and read as printed paper. */
    --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.055'/%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark) {
    :root {
        /* Neutral cool-charcoal canvas so the Columbia blue and red
           read as accents rather than blending into a blue field. Monotonic
           tonal elevation: page is the darkest layer, each surface a step
           lighter, so cards and the navy ribbon both stand off the page. */
        --bg: #14171B;
        --bg-subtle: #1A1E24;
        --surface: #21262D;
        /* The navy ribbon is the single saturated brand moment on the page. */
        --bg-inverse: #103061;
        --dark-blue: #0A2246;        /* hero-gradient partner, deeper than the band */
        --text: #F2F4F8;
        --text-muted: #BCC5D1;
        --text-on-inverse: #F2F6FA;
        --text-on-inverse-muted: #C2D6EE;
        --heading: #F7F9FC;          /* near-white, not blue-tinted; stays above --text */
        --link: #6FB2EA;
        --link-hover: #9AC9F0;
        --accent: #4C9BE6;
        --accent-contrast: #0A0C0F;
        --accent-red: var(--red-bright);
        /* Navbar is a Columbia-blue crown bar (the dark-mode analog of the pale
           blue bar in light mode) -- distinctly bluer than the navy hero, so it
           reads as a raised bar instead of bleeding into the band below. */
        --nav-bg: #16508F;
        --nav-text: #DCEBF8;
        --nav-border: rgba(160, 205, 245, 0.30);
        --nav-logo-hover: var(--text-on-inverse);
        --border: #313841;
        --border-strong: #4C5560;
        /* Translucent so selected runs stay legible over the navy bands,
           cards, and charcoal page alike. */
        --selection-bg: rgba(108, 172, 228, 0.35);
        --shadow-hover: 0 6px 20px rgba(0, 0, 0, 0.55);
    }
}

html {
    font-size: 16px;
}

@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth;
    }
}

body {
    font-family: var(--font-body);
    line-height: 1.7;
    color: var(--text);
    background-color: var(--bg);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

::selection {
    background: var(--selection-bg);
}

/* Accessibility */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.skip-link {
    position: absolute;
    left: -9999px;
    z-index: 200;
    background: var(--surface);
    color: var(--link);
    padding: 0.75rem 1.25rem;
    border-radius: var(--border-radius);
    box-shadow: var(--shadow-hover);
}

.skip-link:focus-visible {
    left: 1rem;
    top: 1rem;
}

/* Container */
.container {
    max-width: var(--container-max-width);
    margin: 0 auto;
    padding: 0 24px;
}

/* Typography */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-heading);
    color: var(--heading);
    line-height: 1.15;
    font-weight: 700;
    letter-spacing: -0.025em;
    margin-bottom: 0.75rem;
}

h1 { font-size: var(--text-3xl); }
h2 { font-size: var(--text-3xl); }
h3 { font-size: var(--text-xl); }

a {
    color: var(--link);
    text-decoration: none;
    transition: color var(--transition);
}

a:hover {
    color: var(--link-hover);
}

p {
    margin-bottom: 1rem;
    color: var(--text-muted);
}

p:last-child {
    margin-bottom: 0;
}

/* Eyebrow / category labels - caps mono against serif headings; crimson so
   the labels carry the one warm accent in an otherwise blue palette */
.category-label,
.section-eyebrow {
    color: var(--accent-red);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: 0.5rem;
    display: block;
}

/* Navigation - Columbia blue brand bar with navy type; deep navy in dark mode */
.site-header {
    background-color: var(--nav-bg);
    border-bottom: 1px solid var(--nav-border);
    position: sticky;
    top: 0;
    z-index: 100;
}

.navbar {
    padding: 0;
}

.nav-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    min-height: 64px;
}

.nav-logo {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-family: var(--font-heading);
    font-weight: 700;
    letter-spacing: -0.01em;
    font-size: 1.25rem;
    color: var(--nav-text);
}

/* The lockup is an <a>, so the global a:hover would repaint the mark and
   wordmark to --link-hover -- invisible on the public bars (where it equals
   --nav-text) but a stray blue on the admin purple bar. Pin hover/focus to
   --nav-text so the logo does not change on hover at all. */
.nav-logo:hover,
.nav-logo:focus-visible {
    color: var(--nav-text);
}

/* The crown mark is inlined SVG drawn in currentColor, so it follows
   --nav-text in all three navbars (navy on light, pale blue on dark,
   lilac in admin). The crown is a neural net, and hover plays a "node
   cascade": nodes fire in sequence up the net (l0 base, l1 middle, apex,
   classed in the markup), each swelling briefly and settling, with the
   apex popping hardest when the signal arrives. The edges stay put --
   at 24px the dots are what the eye resolves, so the outline never
   breaks. It replays per hover entry and simply stops on leave. The
   global prefers-reduced-motion guard zeroes animation durations,
   degrading to a static mark. width is auto: the graph-crown artwork is
   wider than tall, and the viewBox aspect ratio sizes it from the 24px
   height. top nudge: the crown is taller than the wordmark's
   cap-to-baseline ink, so instead of box-centering, seat the bottom node
   row on the text baseline (dots overshoot ~2px like rounded glyphs). */
.logo-img {
    height: 24px;
    width: auto;
    position: relative;
    top: -2px;
    /* The viewBox hugs the resting artwork, and svg clips by default --
       without this the firing nodes flat-top against the viewport edges
       mid-animation. */
    overflow: visible;
}

.logo-img circle {
    transform-box: fill-box;
    transform-origin: center;
}

.nav-logo:hover .logo-img .l0,
.nav-logo:focus-visible .logo-img .l0 {
    animation: logo-node-fire 0.4s ease both;
}

.nav-logo:hover .logo-img .l1,
.nav-logo:focus-visible .logo-img .l1 {
    animation: logo-node-fire 0.4s ease 0.12s both;
}

.nav-logo:hover .logo-img .apex,
.nav-logo:focus-visible .logo-img .apex {
    animation: logo-apex-fire 0.45s ease 0.24s both;
}

@keyframes logo-node-fire {
    0% { transform: scale(1); }
    45% { transform: scale(1.6); }
    100% { transform: scale(1); }
}

@keyframes logo-apex-fire {
    0% { transform: scale(1); }
    40% { transform: scale(1.8); }
    100% { transform: scale(1); }
}

.logo-text {
    white-space: nowrap;
}

.nav-menu {
    display: flex;
    list-style: none;
    gap: 0;
    margin: 0;
    padding: 0;
}

.nav-item {
    margin: 0;
}

.nav-link {
    display: block;
    position: relative;
    color: var(--nav-text);
    font-weight: 500;
    font-size: 0.9375rem;
    padding: 21px 24px;
    /* Labels must not wrap mid-phrase as the bar tightens; the mobile
       breakpoint below is chosen so the row collapses to the hamburger
       before it runs out of room. */
    white-space: nowrap;
    transition: color var(--transition);
}

/* Hover draws an underline beneath the label instead of washing the full
   link area. The bar is currentColor, so it inherits --nav-text in every
   navbar (navy on light, pale blue on dark, lilac in admin) with no
   per-context override -- this is what lets one rule cover the public and
   admin bars. The 24px inset matches the link's horizontal padding so the
   bar spans the label; it fades in on hover rather than sweeping out, which
   reads quieter. The active page gets no special treatment. The color is
   restated because the global
   a:hover rule outranks .nav-link and would tint the label link-blue. The
   prefers-reduced-motion guard zeroes the transition, so it just appears. */
.nav-link::after {
    content: "";
    position: absolute;
    left: 24px;
    right: 24px;
    bottom: 16px;
    height: 2px;
    background-color: currentColor;
    opacity: 0;
    transition: opacity var(--transition);
}

.nav-link:hover,
.nav-link:focus-visible {
    color: var(--nav-text);
}

.nav-link:hover::after,
.nav-link:focus-visible::after {
    opacity: 1;
}

/* Mobile Navigation Toggle */
.nav-toggle {
    display: none;
    background: none;
    border: none;
    cursor: pointer;
    padding: 1rem;
}

.nav-toggle-icon {
    display: block;
    width: 24px;
    height: 2px;
    background-color: var(--nav-text);
    position: relative;
}

.nav-toggle-icon::before,
.nav-toggle-icon::after {
    content: '';
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: var(--nav-text);
    left: 0;
}

.nav-toggle-icon::before { top: -7px; }
.nav-toggle-icon::after { top: 7px; }

/* Nav dropdown (the People menu). The toggle reuses .nav-link so it matches
   the sibling links exactly (padding, color, hover underline); the extra rules
   here only add the caret and reset the <button>'s native chrome. The open
   menu is not a floating card: it drops flush from the bar's bottom edge in
   the bar's own material (same background, no border, squared top corners),
   with the link text on the same 24px grid as the toggle label. Desktop
   reveals the submenu on hover or keyboard focus-within; JS adds .is-open for
   click/tap. */
.nav-item-has-children {
    position: relative;
}

.nav-dropdown-toggle {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    /* The UA button stylesheet resets line-height to normal, which made this
       toggle a shorter box than the sibling <a> links (they inherit the body's
       1.7) and floated the label above theirs. */
    line-height: inherit;
}

/* The sibling links' hover bar spans exactly the label; on the toggle the
   inherited right inset would extend it under the caret (8px) and gap
   (0.4rem), so pull it back to end at the text. */
.nav-dropdown-toggle::after {
    right: calc(24px + 8px + 0.4rem);
}

.nav-caret {
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    transition: transform var(--transition);
}

.nav-dropdown-menu {
    list-style: none;
    margin: 0;
    padding: 0.2rem 0 0.5rem;
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 220px;
    background: var(--nav-bg);
    border-radius: 0 0 var(--border-radius) var(--border-radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
    /* The blur would also bleed upward past the seam with the bar, which
       makes the menu read as a layer floating over the page instead of the
       bar folding down. Clip the shadow at the top edge only; it still
       falls on the sides and below. */
    clip-path: inset(0 -40px -40px -40px);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition), visibility var(--transition);
    z-index: 200;
}

.nav-item-has-children:hover .nav-dropdown-menu,
.nav-item-has-children:focus-within .nav-dropdown-menu,
.nav-item-has-children.is-open .nav-dropdown-menu {
    opacity: 1;
    visibility: visible;
}

.nav-item-has-children:hover .nav-caret,
.nav-item-has-children:focus-within .nav-caret,
.nav-item-has-children.is-open .nav-caret {
    transform: rotate(180deg);
}

/* The last item's menu is wider than its toggle and sits at the bar's right
   edge; left-anchored it would hang past the viewport -- and even closed
   (visibility:hidden keeps it in layout) it widens the page and causes a
   horizontal scrollbar. Anchor it to the toggle's right edge instead. */
.nav-item-has-children:last-child .nav-dropdown-menu {
    left: auto;
    right: 0;
}

.nav-dropdown-link {
    display: block;
    padding: 0.55rem 24px;
    color: var(--nav-text);
    font-size: 0.9rem;
    font-weight: 500;
    white-space: nowrap;
    transition: background-color var(--transition), color var(--transition);
}

.nav-dropdown-link:hover,
.nav-dropdown-link:focus-visible {
    color: var(--nav-text);
    background-color: color-mix(in srgb, var(--nav-text) 10%, transparent);
}

/* Main Content */
.main-content {
    flex: 1;
}

/* Hero - "Midnight Field": a full-viewport deep-navy loss landscape in BOTH
   color schemes (light mode opens dark before dropping to the white page).
   Grotesque display type sits over the field; the contour drawing covers the
   whole band edge to edge. */
.hero {
    position: relative;
    min-height: calc(100svh - 64px);
    display: flex;
    align-items: center;
    padding: 4rem 0;
    overflow: hidden;
}

/* One midnight field across every band: the hero and the subpage headers
   share the same navy gradient (grain + pale-blue glows) in both color
   schemes, so the site opens and continues in a single visual world. */
.hero,
.page-header,
.not-found-hero {
    background-image:
        var(--grain),
        radial-gradient(58% 88% at 90% 6%, color-mix(in srgb, var(--pale-blue) 18%, transparent) 0%, transparent 60%),
        radial-gradient(56% 84% at 3% 105%, color-mix(in srgb, var(--pale-blue) 12%, transparent) 0%, transparent 60%),
        linear-gradient(135deg, #0A1F3D 0%, #05132B 100%);
}

/* Loss-landscape contours: level sets with a gradient-descent path. The SVG
   keeps its fixed 880x620 field but covers the full band via slice scaling
   (preserveAspectRatio on the element); a left-edge feather keeps the title
   legible over the drawing. */
.hero-contours {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    pointer-events: none;
    -webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.3) 0%, #000 52%);
    mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.3) 0%, #000 52%);
}

/* The 3D hero canvas (inserted by hero3d.js next to the SVG, which stays
   empty on that path as the fallback renderer) inherits the whole band
   treatment -- inset, z-index, feather mask -- from .hero-contours above. */

/* While hand-spinning the terrain (drag left/right), suppress text
   selection so a drag across the headline doesn't highlight it. */
.hero.is-dragging {
    cursor: grabbing;
    user-select: none;
    -webkit-user-select: none;
}

/* Slice scaling blows the drawing up ~1.6x; thin the strokes back down so
   the lines keep their survey-map weight at full bleed. */
.hero-contours .hc-ring {
    stroke-width: 0.9;
    opacity: 0.5;
}

.hero-contours .hc-ring-inner {
    opacity: 0.8;
}

.hero-contours .hc-path {
    stroke-width: 2;
}

/* Interior page headers carry the same generator, quieter: it sits behind
   the title as a signature rather than competing with it. Inking (light/dark)
   is inherited from the shared .hc-* rules below. */
.page-header {
    position: relative;
    overflow: hidden;
}

.page-header .container {
    position: relative;
    z-index: 1;
}

.header-contours {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    pointer-events: none;
    /* Feather the left edge so the topography reads under the title without a
       hard block behind the text; the rest of the band is full-bleed. */
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 26%);
    mask-image: linear-gradient(90deg, transparent 0%, #000 26%);
}

/* Subpages carry the landscape alone -- contour rings only. The descent path
   (and its marker/ping) is reserved for the home hero, where it's the
   interactive punchline; here the topography is the quiet signature, pulled
   back from the hero's full strength so the title owns the band. Stroke width
   matches the hero's slice-scaled lines. */
.header-contours .hc-ring {
    stroke-width: 0.9;
    opacity: 0.14;
}

.header-contours .hc-ring-inner {
    opacity: 0.26;
}

.header-contours .hc-descent,
.header-contours .hc-min,
.header-contours .hc-ping {
    display: none;
}

.hc-ring {
    fill: none;
    stroke: var(--pale-blue);
    stroke-width: 1.5;
    opacity: 0.6;
}

.hc-ring-inner {
    opacity: 0.85;
}

.hc-path {
    fill: none;
    stroke: var(--red-bright);
    stroke-width: 2.8;
    stroke-dasharray: 1 6;
    stroke-linecap: round;
    opacity: 0.95;
}

/* The draw-on reveal and the minimum marker's delayed fade-in are driven
   from main.js via the Web Animations API (with an explicit hidden inline
   style set before first paint), including the reduced-motion fallback.
   Only static presentation lives here. */
.hc-min {
    fill: var(--red-bright);
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
}

/* One-time expanding ring when the descent settles at the minimum */
.hc-ping {
    fill: none;
    stroke: var(--red-bright);
    stroke-width: 1.5;
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
}

/* Just wide enough to cover the 1.5px dashed trace: a wider reveal
   corridor uncovers later path segments early wherever the trajectory
   crosses itself. */
.hc-reveal-path {
    fill: none;
    stroke: #fff;
    stroke-width: 5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Hero copy sits in the standard content column (same left margin as the
   navbar logo). width:100% matters: as a flex item of the centered hero the
   container would otherwise shrink-wrap to its text and float toward the
   middle of the band instead of aligning with the site column. The field's
   action (wells + descent) is clamped to the right portion of the band in
   main.js, so text and graphic each own their side. */
.hero .container {
    position: relative;
    z-index: 1;
    width: 100%;
}

.hero-eyebrow {
    color: var(--text-on-inverse-muted);
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    display: block;
    margin-bottom: 1.25rem;
}

.hero-title {
    color: var(--text-on-inverse);
    font-size: var(--text-display);
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.02;
    max-width: 16ch;
    margin-bottom: 1.25rem;
}

.hero-tagline {
    font-size: var(--text-xl);
    color: var(--text-on-inverse-muted);
    max-width: 42ch;
    margin-bottom: 2.5rem;
}

.hero-actions {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

/* Optimizer readout: a small mono HUD in the field's corner that ticks step
   count and loss while the descent draws, then parks at theta-star. The hint
   line surfaces the click-to-relaunch easter egg. pointer-events: none so
   clicks fall straight through to the relaunch handler; hidden until the
   first descent actually renders (main.js adds .active). */
.hero-hud {
    position: absolute;
    right: 2.5rem;
    bottom: 2rem;
    z-index: 1;
    visibility: hidden;
    pointer-events: none;
    text-align: right;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    font-variant-numeric: tabular-nums;
    color: var(--text-on-inverse-muted);
}

.hero-hud.active {
    visibility: visible;
}

.hero-hud-readout {
    display: block;
    opacity: 0.55;
}

/* The sliders are real controls, so they opt back into pointer events
   (the HUD container passes clicks through to the relaunch handler).
   Styled as hairline instruments -- 1px track, small dim thumb, the whole
   block faded until engaged -- so the eye reads the field first and finds
   the controls only when it goes looking. */
.hero-hud-controls {
    pointer-events: auto;
    margin-bottom: 0.7rem;
    opacity: 0.5;
    transition: opacity var(--transition);
}

.hero-hud-controls:hover,
.hero-hud-controls:focus-within {
    opacity: 1;
}

.hero-hud-control {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 0.35rem;
    cursor: default;
}

.hero-hud-control input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    width: 110px;
    height: 12px;
    margin: 0;
    background: transparent;
    cursor: pointer;
}

.hero-hud-control input[type="range"]::-webkit-slider-runnable-track {
    height: 1px;
    background: color-mix(in srgb, var(--pale-blue) 40%, transparent);
}

.hero-hud-control input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 7px;
    height: 7px;
    margin-top: -3px;
    border-radius: 50%;
    background: var(--pale-blue);
}

.hero-hud-control input[type="range"]::-moz-range-track {
    height: 1px;
    background: color-mix(in srgb, var(--pale-blue) 40%, transparent);
}

.hero-hud-control input[type="range"]::-moz-range-thumb {
    width: 7px;
    height: 7px;
    border: none;
    border-radius: 50%;
    background: var(--pale-blue);
}

/* Play/pause for the 3D hero's auto camera motion: a small glyph sitting to
   the right of the loss readout, in the same dim register (bright on hover).
   Revealed by hero3d.js (the flat SVG hero has no camera to pause); the
   pause bars swap to a play triangle via aria-pressed. */
.hero-hud-status {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.55rem;
}

.hero-hud-pause {
    pointer-events: auto;
    display: inline-flex;
    padding: 2px;
    border: 0;
    background: none;
    color: inherit;
    opacity: 0.55;
    cursor: pointer;
    transition: opacity var(--transition);
}

.hero-hud-pause svg {
    width: 9px;
    height: 9px;
    display: block;
    fill: currentColor;
}

.hero-hud-pause .hero-hud-pause-tri,
.hero-hud-pause[aria-pressed="true"] .hero-hud-pause-bars {
    display: none;
}

.hero-hud-pause[aria-pressed="true"] .hero-hud-pause-tri {
    display: block;
}

.hero-hud-pause:hover,
.hero-hud-pause:focus-visible {
    opacity: 1;
}

.btn {
    display: inline-block;
    font-weight: 600;
    font-size: var(--text-base);
    padding: 0.75rem 1.75rem;
    border-radius: var(--border-radius);
    transition: background-color var(--transition), border-color var(--transition), color var(--transition);
}

.btn-primary {
    background: var(--text-on-inverse);
    color: var(--dark-blue);
    border: 1px solid var(--text-on-inverse);
}

.btn-primary:hover {
    background: var(--light-blue);
    border-color: var(--light-blue);
    color: var(--dark-blue);
}

.btn-ghost {
    border: 1px solid rgba(255, 255, 255, 0.4);
    color: var(--text-on-inverse);
}

.btn-ghost:hover {
    border-color: var(--text-on-inverse);
    color: var(--text-on-inverse);
    background: rgba(255, 255, 255, 0.08);
}

/* Section pattern */
.section,
.mission-section,
.people-section,
.events-section {
    padding: 5rem 0;
    background-color: var(--bg);
}

/* Contain the hover cards' off-screen resting positions so they don't widen
   the page. Clip (not hidden) creates no scroll container, so vertical flow and
   the sticky header are unaffected; flipped cards stay within the viewport so
   nothing that should show gets clipped. */
.people-section {
    overflow-x: clip;
}

.section-alt {
    background-color: var(--bg-subtle);
}

.section-title {
    font-size: var(--text-3xl);
    margin-bottom: 1.5rem;
}

/* The homepage's lead section steps up one tier (see --text-4xl) so the
   page has a real type ramp below the hero. Later sections stay at the
   base section-title size. */
.home-people .section-title {
    font-size: var(--text-4xl);
}

.section-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
}

.section-header .section-title {
    margin-bottom: 0;
}

.section-header .section-more {
    padding-bottom: 0.3rem;
}

.section-more {
    font-weight: 600;
    font-size: var(--text-sm);
    white-space: nowrap;
}

.section-more::after {
    content: ' \2192';
}

/* Mission: not a card -- the mission is the page's one large typographic
   statement, set in the display face at full ink so it reads as a claim,
   not a paragraph of body copy. */
.mission-statement {
    max-width: 56rem;
}

.mission-content {
    font-family: var(--font-heading);
    font-size: var(--text-statement);
    font-weight: 500;
    letter-spacing: -0.015em;
    line-height: 1.45;
    color: var(--heading);
}

.mission-content p {
    color: inherit;
}

/* The upcoming-events section keeps its own (subtle) background but gains
   faint concentric contour circles sweeping in from the top-right, echoing
   the hero's loss landscape. */
.home-events {
    background-image:
        repeating-radial-gradient(circle at 88% 22%, transparent 0 70px, rgba(0, 114, 206, 0.075) 70px 71px);
}

@media (prefers-color-scheme: dark) {
    .home-events {
        background-image:
            repeating-radial-gradient(circle at 88% 22%, transparent 0 70px, rgba(108, 172, 228, 0.07) 70px 71px);
    }
}

/* Homepage - upcoming events preview. Steps down cleanly instead of leaving an
   orphan card: 3-across, then drop the 3rd and show 2-across, then stack the
   remaining 2 into a single column. */
.home-events-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
    margin-top: 1.5rem;
}

/* Collapse the track to the number of cards so 1 or 2 events fill the row
   instead of leaving empty grid cells. A lone card spans full width; two
   cards split the row evenly. */
.home-events-grid:has(.event-card:last-child:nth-child(1)) {
    grid-template-columns: 1fr;
}

.home-events-grid:has(.event-card:last-child:nth-child(2)) {
    grid-template-columns: repeat(2, 1fr);
}

/* Too narrow for 3 across: hide the 3rd and show a tidy 2-across row. */
@media (max-width: 960px) {
    .home-events-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .home-events-grid .event-card:nth-child(3) {
        display: none;
    }
}

/* Mobile: stack the two remaining cards in a single column (2 rows). The 3rd
   stays hidden via the rule above. */
@media (max-width: 640px) {
    .home-events-grid {
        grid-template-columns: 1fr;
    }
}

.no-upcoming {
    margin-top: 1.5rem;
    color: var(--text-muted);
}

/* Intro line above the recent-talks fallback grid (shown when nothing is
   upcoming). Sits between the section header and the cards. */
.home-events-note {
    margin-top: 0.75rem;
    color: var(--text-muted);
}

.home-events-note + .home-events-grid {
    margin-top: 1.25rem;
}

/* Homepage - stats and photo strip */
.stats-row {
    display: flex;
    gap: 4rem;
    flex-wrap: wrap;
    margin: 2rem 0 2.5rem;
}

.stat-number {
    font-family: var(--font-heading);
    font-size: var(--text-3xl);
    font-weight: 600;
    color: var(--heading);
    line-height: 1.1;
    display: block;
    font-variant-numeric: tabular-nums;
}

.stat-label {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-muted);
}

.photo-strip {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    margin-bottom: 1.1rem;
}

.photo-row {
    display: inline-flex;
    align-items: center;
    padding-left: 14px;
}

/* Nest the second row into the first like pebbles: pull it up and shift it
   half a face so its circles sit in the gaps of the row above. */
.photo-row + .photo-row {
    margin-top: -10px;
    transform: translateX(21px);
}

/* Each face is one tile of the single montage sheet (the people_montage
   route); the sheet URL and grid shape arrive as CSS variables on the strip,
   and per-face inline background-position picks the tile. */
.strip-face {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: 3px solid var(--bg-subtle);
    background-image: var(--montage-image);
    background-size: calc(var(--montage-cols) * 100%) calc(var(--montage-rows) * 100%);
    background-repeat: no-repeat;
    margin-left: -14px;
    backface-visibility: hidden;
    transition: transform var(--transition);
}

.photo-strip:hover .strip-face {
    transform: translateY(-2px);
}

/* A face swapping in a new person spins a half-turn on its vertical axis;
   the tile is switched at the midpoint (see main.js) so the flip hides the cut. */
.strip-face.is-rotating {
    transform: rotateY(90deg);
}

.home-people-columns {
    display: grid;
    grid-template-columns: 1fr minmax(260px, 420px);
    gap: 4rem;
    align-items: start;
}

/* Research-area index: static two-column numbered list of what the lab
   studies, mono red counters in the margin. */
.area-index {
    columns: 2;
    column-gap: 2.6rem;
    margin-top: 0.5rem;
}

.area-index a {
    display: flex;
    align-items: baseline;
    gap: 0.7rem;
    padding: 0.42rem 0;
    text-decoration: none;
    break-inside: avoid;
}

.area-index-number {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--accent-red);
    min-width: 1.6em;
}

.area-index-name {
    font-family: var(--font-heading);
    font-size: var(--text-base);
    color: var(--text);
    white-space: nowrap;
    transition: color var(--transition);
}

/* Once the column tightens, long names must wrap instead of spilling into
   the neighboring column. */
@media (max-width: 900px) {
    .area-index-name {
        white-space: normal;
    }
}

.area-index a:hover .area-index-name,
.area-index a:focus-visible .area-index-name {
    color: var(--accent-red);
}

/* Keep People + Research Areas side by side down to phone width. Close the large
   gap and pull the right column narrower as things tighten. */
@media (max-width: 900px) {
    .home-people-columns {
        grid-template-columns: 1fr minmax(220px, 380px);
        gap: 2.5rem;
    }
}

@media (max-width: 768px) {
    .home-people-columns {
        gap: 1.75rem;
    }

    /* Shrink the overlapping avatars so the People column's minimum width stays
       small enough to keep both columns side by side this narrow. */
    .strip-face {
        width: 46px;
        height: 46px;
        margin-left: -12px;
    }

    .photo-row {
        padding-left: 12px;
    }

    .photo-row + .photo-row {
        margin-top: -8px;
        transform: translateX(17px);
    }
}

/* Phone: only now stack into a single column. */
@media (max-width: 640px) {
    .home-people-columns {
        grid-template-columns: 1fr;
        gap: 2.5rem;
    }
}

/* Page Header - inverse band (background shared with .hero above) */
.page-header {
    padding: 4rem 0;
}

.page-header .section-eyebrow {
    color: var(--text-on-inverse-muted);
}

.event-back {
    margin: 0 0 1.5rem;
}

.page-title {
    color: var(--text-on-inverse);
    margin-bottom: 0;
}

.page-subtitle {
    color: var(--text-on-inverse-muted);
    font-size: var(--text-lg);
    margin: 0.5rem 0 0;
}

/* Calendar subscribe: a quiet line between the upcoming and past events */
.calendar-subscribe {
    position: relative;
    margin: 0 0 2rem;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.calendar-subscribe-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font: inherit;
    color: var(--link);
    background: none;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.45rem 0.85rem;
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition), background var(--transition);
}

.calendar-subscribe-toggle:hover,
.calendar-subscribe-toggle[aria-expanded="true"] {
    color: var(--link-hover);
    border-color: var(--link);
}

.calendar-subscribe-toggle .calendar-subscribe-label {
    color: inherit;
    margin-right: 0;
}

.calendar-subscribe-caret {
    display: inline-block;
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    transition: transform var(--transition);
}

.calendar-subscribe-toggle[aria-expanded="true"] .calendar-subscribe-caret {
    transform: rotate(180deg);
}

.calendar-subscribe-label {
    font-family: var(--font-mono);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-right: 0.85rem;
}

.calendar-subscribe-panel {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    z-index: 20;
    min-width: max-content;
    max-width: min(22rem, 90vw);
    padding: 1rem 1.15rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--border-radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

.calendar-subscribe-panel[hidden] {
    display: none;
}

.calendar-subscribe-step {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    margin: 0 0 0.55rem;
}

.calendar-subscribe-step + .calendar-subscribe-links,
.calendar-subscribe-types + .calendar-subscribe-step {
    margin-top: 0;
}

.calendar-subscribe-types + .calendar-subscribe-step {
    padding-top: 0.9rem;
    border-top: 1px solid var(--border);
}

.calendar-subscribe-types {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0 0 0.9rem;
    padding: 0;
    border: none;
}

.calendar-subscribe-types label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    color: var(--text);
}

.calendar-subscribe-types input {
    margin: 0;
    accent-color: var(--link);
}

.calendar-subscribe-panel .calendar-subscribe-links {
    white-space: nowrap;
}

.calendar-subscribe-links a,
.calendar-copy {
    font: inherit;
    color: var(--link);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    transition: color var(--transition);
}

.calendar-subscribe-links a:hover,
.calendar-copy:hover {
    color: var(--link-hover);
}

.calendar-subscribe-links > * + *::before {
    content: "\00B7";
    margin: 0 0.6rem;
    color: var(--border);
}

.calendar-copy.is-copied {
    color: var(--accent);
}

/* Event detail page */
.event-detail-body {
    max-width: 800px;
}

.event-detail-title {
    margin-bottom: 0.5rem;
}

.event-detail-speaker {
    font-weight: 600;
    color: var(--text);
    margin-bottom: 1rem;
}

.event-detail .event-meta {
    font-size: var(--text-base);
    margin-bottom: 1.5rem;
}

.event-detail-abstract {
    margin-bottom: 1.5rem;
}

.event-detail-bio {
    margin-bottom: 1.5rem;
}

.event-detail-bio-heading {
    font-size: var(--text-base);
    margin-bottom: 0.5rem;
}


/* People Section */
.tier-section {
    margin-bottom: 4rem;
}

.tier-section:last-child {
    margin-bottom: 0;
}

.tier-label,
.tier-section .category-label {
    font-size: var(--text-sm);
}

.tier-title {
    margin-bottom: 2rem;
}

/* Appended to a tier heading while a research-area filter is active, e.g.
   "Core Faculty in Vision", so it's clear the list is drilled into an area. */
.tier-title-note {
    color: var(--text-muted);
    font-weight: 400;
}

.tier-title-note .tier-title-area {
    color: var(--accent-red);
}

.tier-description {
    color: var(--text-muted);
    margin-bottom: 2rem;
    display: none;
}

/* People page: research-area filter. Serif text toggles echoing the homepage
   "Research Areas" list -- muted by default, accent-underlined when active. */
.people-filter {
    margin-bottom: 3.5rem;
    padding-bottom: 2rem;
    border-bottom: 1px solid var(--border);
}

.people-filter-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.85rem 2.5rem;
}

.people-filter-chip {
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    color: var(--text-muted);
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    padding: 0.1rem 0;
    cursor: pointer;
    transition: color var(--transition), border-color var(--transition);
}

.people-filter-chip:hover {
    color: var(--text);
    /* Faint underline on hover, full color when active - same affordance
       language as the navbar. Red marks a drilled-into area; "All Areas"
       is the neutral default, so it keeps the blue accent. */
    border-bottom-color: color-mix(in srgb, var(--accent-red) 45%, transparent);
}

.people-filter-chip.is-active {
    color: var(--accent-red);
    border-bottom-color: var(--accent-red);
}

.people-filter-chip[data-area=""]:hover {
    border-bottom-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

.people-filter-chip[data-area=""].is-active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.people-filter-empty {
    color: var(--text-muted);
    text-align: center;
    padding: 2rem 0;
}

/* Mobile alternative to the chip row: a single native dropdown. Hidden on
   desktop (chips shown), swapped in under the mobile breakpoint below. */
.people-filter-select-wrap {
    display: none;
}

.people-filter-select {
    width: 100%;
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    color: var(--text);
    background-color: var(--bg);
    /* Custom chevron so the control reads as a dropdown without an external
       asset; keep the strict self-contained styling used elsewhere here. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9' fill='none'%3E%3Cpath d='M1 1L7 7L13 1' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 1rem center;
    border: 1px solid var(--border);
    border-radius: var(--border-radius);
    padding: 0.7rem 2.75rem 0.7rem 1rem;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    cursor: pointer;
}

/* People: staggered circle grid. Circles span 2 tracks; alternate rows
   hold one fewer circle and start one track in, giving a half-cell offset. */
.people-honeycomb {
    display: grid;
    /* minmax(0, 1fr): without the 0 floor, a track won't shrink below its
       content's min-content width, so one long unbreakable name (see
       .tile-name's overflow-wrap below) could force its column wider than its
       fair share and overlap the neighboring tile. */
    grid-template-columns: repeat(10, minmax(0, 1fr));
    row-gap: 3rem;
}

/* While filtering, hidden cells are removed and the stagger is dropped so the
   visible matches pack into a clean uniform grid (no nth-child gaps). */
.person-cell.is-hidden {
    display: none;
}

.people-honeycomb.is-filtering .person-cell {
    grid-column: auto / span 2;
}

.person-cell {
    grid-column: auto / span 2;
    display: flex;
    justify-content: center;
    position: relative;
}

.person-cell:hover {
    z-index: 5;
}

.person-tile {
    /* Photo size lives in a var so the hover card can center itself on the
       portrait (top: half the photo) at every breakpoint. */
    --tile-photo-size: 152px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.85rem;
    text-align: center;
    max-width: 190px;
    position: relative;
}

.tile-photo {
    width: var(--tile-photo-size);
    height: var(--tile-photo-size);
    /* Above the card so the card can slide out from behind it. */
    position: relative;
    z-index: 2;
}

.tile-photo img,
.tile-photo .photo-placeholder {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: var(--border-radius);
    display: block;
    /* Same easing/duration as the card so photo and card move together. */
    transition: transform var(--transition);
}

/* Monogram fallback when a person has no headshot. Two initials (first + last)
   on a Columbia-blue-family wash. The wash is SEEDED per person (the
   `wash_variant` template filter hashes the name to .pp-w0..4) so neighboring
   placeholders differ in tint and gradient angle the way real photos differ -
   a run of identical tiles reads as a hole in the grid, varied ones read as
   part of it. All five stay in one quiet blue register per mode (pale washes
   with navy initials in light, navy slates with pale initials in dark).

   Scoped to .tile-photo so these visuals (and the flex centering that must
   override the display:block on the shared sizing rule above) don't leak onto
   the Get Involved director placeholder, which styles itself separately. */
.tile-photo .photo-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-heading);
    /* Scale the monogram with the tile so it fills each breakpoint evenly. */
    font-size: calc(var(--tile-photo-size) * 0.3);
    font-weight: 600;
    letter-spacing: 0.01em;
    color: #0E3568;
    background: linear-gradient(145deg, #DCEBF7 0%, #B9D9EB 52%, #9CC4E4 100%);
    box-shadow: inset 0 0 0 1px rgba(0, 48, 135, 0.16);
}

.tile-photo .photo-placeholder.pp-w1 {
    background: linear-gradient(160deg, #DFE9F1 0%, #BCCEDE 52%, #9FB8CE 100%);
}

.tile-photo .photo-placeholder.pp-w2 {
    background: linear-gradient(135deg, #D3E5F5 0%, #ACCBE8 52%, #8DB4DA 100%);
}

.tile-photo .photo-placeholder.pp-w3 {
    background: linear-gradient(150deg, #DEE8EC 0%, #BFD3DB 52%, #A4BDC9 100%);
}

.tile-photo .photo-placeholder.pp-w4 {
    background: linear-gradient(125deg, #DCE6F6 0%, #B9C9EA 52%, #9DB2DE 100%);
}

.tile-photo .photo-placeholder .pp-initials {
    display: block;
    line-height: 1;
    /* Optical centering: caps sit a hair high in the line box. */
    transform: translateY(0.04em);
}

/* Dark mode: a navy slate tile with pale-blue initials - the inverse of the
   light pairing, and the same navy/pale-blue language as the hero and navbar.
   Set explicitly rather than via --dark-blue (which flips dark and would sink
   the initials into the fill). */
@media (prefers-color-scheme: dark) {
    .tile-photo .photo-placeholder {
        color: #B0D2F2;
        background: linear-gradient(145deg, #294863 0%, #16283C 100%);
        box-shadow: inset 0 0 0 1px rgba(160, 205, 245, 0.18);
    }

    .tile-photo .photo-placeholder.pp-w1 {
        background: linear-gradient(160deg, #2C4258 0%, #17222E 100%);
    }

    .tile-photo .photo-placeholder.pp-w2 {
        background: linear-gradient(135deg, #24486E 0%, #122641 100%);
    }

    .tile-photo .photo-placeholder.pp-w3 {
        background: linear-gradient(150deg, #2E4A5E 0%, #182832 100%);
    }

    .tile-photo .photo-placeholder.pp-w4 {
        background: linear-gradient(125deg, #2A4066 0%, #151F38 100%);
    }
}

.tile-name {
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--heading);
    line-height: 1.2;
    letter-spacing: -0.005em;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-feature-settings: "kern" 1, "liga" 1;
    text-wrap: balance;
    /* text-wrap: balance only redistributes text across existing spaces; a
       long unbroken surname (e.g. "Rungratsameetaweemana") needs this to
       break at all, or it forces the tile - and its grid track - wider than
       intended and overlaps the neighboring tile on narrow viewports. */
    overflow-wrap: anywhere;
    /* Ease the name color in step with the photo's scale; without this the
       name snaps to blue while the photo animates smoothly. */
    transition: color var(--transition);
}

/* Scoped to links: tiles without a website still show the hover card, but
   the name keeping its resting color signals there is nothing to click. */
a.person-tile:hover .tile-name,
a.person-tile:focus-visible .tile-name {
    color: var(--link);
}

/* Rank line under the name on the flat faculty list (Steering Committee /
   SEAS Faculty / Affiliate): tracked-out caps, smaller and fainter than
   --text-muted so it reads as a classification label, not a subtitle.
   Pulled up against the name: the tile's flex gap separates photo / name /
   rank equally, but the rank belongs to the name. */
.tile-rank {
    font-size: 0.6875rem;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: color-mix(in srgb, var(--text-muted) 72%, transparent);
    line-height: 1.3;
    margin-top: -0.5rem;
    overflow-wrap: anywhere;
}

/* Archival annotation card: title, department, and research areas. Hidden at
   rest (and on touch, where it can't be summoned without stealing the tile's
   tap) so the honeycomb stays clean; on pointer devices it fades in beside the
   portrait when the tile is hovered or keyboard-focused. Absolutely positioned
   so it never reflows the grid; the hovered .person-cell already gets z-index
   so it floats above neighbours.

   Sits to the RIGHT of the portrait and vertically centered on it, reading as
   a caption extending off the photo. JS adds .flip-left near the viewport's
   right edge so it mirrors to the left instead of clipping. A softly tinted
   panel (--bg-subtle) with serif title, muted department, and a mono eyebrow
   in the site's .section-eyebrow language. */
.tile-card {
    display: none;
}

@media (hover: hover) and (pointer: fine) {
    .tile-card {
        display: block;
        position: absolute;
        /* A rounded card beside the portrait, with a small gap. Sits on top and
           simply fades in on hover - no slide. */
        top: 0;
        left: calc(50% + var(--tile-photo-size) / 2 + 0.6rem);
        width: max-content;
        max-width: 16.5rem;
        /* Exactly the photo's height so every card lines up with its portrait;
           content is centred, and the widened max-width keeps long area lists
           to a few lines so they fit without clipping. */
        height: var(--tile-photo-size);
        overflow: hidden;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        justify-content: center;
        padding: 0.8rem 1.05rem;
        text-align: left;
        /* Softly tinted panel (the honeycomb packs tiles too tightly for a bare
           side-caption). Blended toward --heading so the edge reads against the
           page without a border: a Columbia blue-gray in light, a medium slate
           in dark - both clearly separated from the page since the tokens flip. */
        background: color-mix(in srgb, var(--bg-subtle) 90%, var(--heading) 10%);
        border-radius: var(--border-radius);
        box-shadow: var(--shadow-hover);
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        z-index: 6;
        transition: opacity var(--transition), visibility var(--transition);
    }

    /* Mirror to the left of the portrait when the right edge would clip. */
    .person-tile.flip-left .tile-card {
        left: auto;
        right: calc(50% + var(--tile-photo-size) / 2 + 0.6rem);
        text-align: right;
    }

    .person-tile:hover .tile-card,
    .person-tile:focus-visible .tile-card {
        opacity: 1;
        visibility: visible;
    }

    /* Subtle scale on the portrait as hover feedback (elevation language:
       no drop shadow on in-flow content; the floating hover card carries
       the overlay shadow). */
    .person-tile:hover .tile-photo img,
    .person-tile:focus-visible .tile-photo img,
    .person-tile:hover .tile-photo .photo-placeholder,
    .person-tile:focus-visible .tile-photo .photo-placeholder {
        transform: scale(1.03);
    }
}

.tile-role {
    display: block;
}

.tile-title {
    display: block;
    font-family: var(--font-heading);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--heading);
    line-height: 1.3;
}

.tile-dept {
    display: block;
    font-size: var(--text-xs);
    color: var(--text-muted);
    line-height: 1.35;
    margin-top: 0.1rem;
}

.tile-research {
    display: block;
    margin-top: 0.6rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--border);
}

/* When a person has no title/department, research leads the card - drop the
   orphaned divider. */
.tile-research:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

/* Advisor line (students): same divided-section treatment as research, so a
   student's card reads role -> advisor the way a faculty card reads role ->
   research. */
.tile-advisor {
    display: block;
    margin-top: 0.6rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--border);
}

.tile-advisor:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

.tile-advisor-names {
    display: block;
    font-size: var(--text-xs);
    color: var(--text-muted);
    line-height: 1.5;
    text-wrap: balance;
}

.tile-eyebrow {
    display: block;
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--accent-red);
    margin-bottom: 0.3rem;
}

.tile-areas {
    display: block;
    font-size: var(--text-xs);
    color: var(--text-muted);
    line-height: 1.5;
    text-wrap: balance;
}

/* Desktop: rows alternate 5 and 4 circles (period 9) */
@media (min-width: 901px) {
    .person-cell:nth-child(9n+6) {
        grid-column: 2 / span 2;
    }

    .person-cell:nth-child(9n+7) {
        grid-column: 4 / span 2;
    }

    .person-cell:nth-child(9n+8) {
        grid-column: 6 / span 2;
    }

    .person-cell:nth-child(9n+9) {
        grid-column: 8 / span 2;
    }
}

/* Tablet: rows alternate 4 and 3 (period 7) */
@media (min-width: 601px) and (max-width: 900px) {
    .people-honeycomb {
        grid-template-columns: repeat(8, minmax(0, 1fr));
    }

    .person-tile {
        --tile-photo-size: 124px;
    }

    .person-cell:nth-child(7n+5) {
        grid-column: 2 / span 2;
    }

    .person-cell:nth-child(7n+6) {
        grid-column: 4 / span 2;
    }

    .person-cell:nth-child(7n+7) {
        grid-column: 6 / span 2;
    }
}

/* Phones: rows alternate 3 and 2 (period 5) */
@media (max-width: 600px) {
    .people-honeycomb {
        grid-template-columns: repeat(6, minmax(0, 1fr));
        row-gap: 2rem;
    }

    .person-tile {
        --tile-photo-size: 96px;
        /* Shrink the text cap along with the photo so the name/rank wrap at
           a width proportional to the smaller tile instead of keeping the
           desktop-scaled 190px allowance (see .tile-name overflow-wrap). */
        max-width: 120px;
    }

    .person-cell:nth-child(5n+4) {
        grid-column: 2 / span 2;
    }

    .person-cell:nth-child(5n+5) {
        grid-column: 4 / span 2;
    }
}

/* Dense variant for the student-group tiers (postdocs + PhD students on
   /people/students): the same staggered circle grid at roughly two-thirds
   scale, since those tiers hold ~190 people. Faculty tiers keep the full-size
   grid. Each breakpoint fits one row more than the base grid and restates the
   stagger with its own nth-child period; the base grid's offsets are reset
   first so the two periods don't interleave. The :not(.is-filtering) guards
   keep the filtered state packing into a uniform grid, same as the base. */
.people-honeycomb-dense {
    row-gap: 2.25rem;
}

.people-honeycomb-dense .person-tile {
    --tile-photo-size: 104px;
    max-width: 136px;
}

.people-honeycomb-dense .tile-name {
    font-size: var(--text-base);
}

/* The base hover card locks its height to the portrait, which fits the
   152px tiles but clips the card's content (role + advisor runs ~114px)
   behind the overflow:hidden at this scale. Size to content instead, and
   pull the padding in a notch to match the smaller tile. */
.people-honeycomb-dense .tile-card {
    height: auto;
    min-height: var(--tile-photo-size);
    padding: 0.7rem 0.9rem;
}

/* Desktop: rows alternate 8 and 7 circles (period 15) */
@media (min-width: 901px) {
    .people-honeycomb-dense {
        grid-template-columns: repeat(16, minmax(0, 1fr));
    }

    .people-honeycomb-dense .person-cell {
        grid-column: auto / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(15n+9) {
        grid-column: 2 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(15n+10) {
        grid-column: 4 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(15n+11) {
        grid-column: 6 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(15n+12) {
        grid-column: 8 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(15n+13) {
        grid-column: 10 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(15n+14) {
        grid-column: 12 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(15n+15) {
        grid-column: 14 / span 2;
    }
}

/* Tablet: rows alternate 6 and 5 (period 11) */
@media (min-width: 601px) and (max-width: 900px) {
    .people-honeycomb-dense {
        grid-template-columns: repeat(12, minmax(0, 1fr));
        row-gap: 2rem;
    }

    .people-honeycomb-dense .person-tile {
        --tile-photo-size: 88px;
        max-width: 116px;
    }

    .people-honeycomb-dense .tile-name {
        font-size: var(--text-sm);
    }

    .people-honeycomb-dense .person-cell {
        grid-column: auto / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(11n+7) {
        grid-column: 2 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(11n+8) {
        grid-column: 4 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(11n+9) {
        grid-column: 6 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(11n+10) {
        grid-column: 8 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(11n+11) {
        grid-column: 10 / span 2;
    }
}

/* Phones: rows alternate 4 and 3 (period 7) */
@media (max-width: 600px) {
    .people-honeycomb-dense {
        grid-template-columns: repeat(8, minmax(0, 1fr));
        row-gap: 1.6rem;
    }

    .people-honeycomb-dense .person-tile {
        --tile-photo-size: 76px;
        max-width: 92px;
    }

    .people-honeycomb-dense .tile-name {
        font-size: 0.85rem;
    }

    .people-honeycomb-dense .person-cell {
        grid-column: auto / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(7n+5) {
        grid-column: 2 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(7n+6) {
        grid-column: 4 / span 2;
    }

    .people-honeycomb-dense:not(.is-filtering) .person-cell:nth-child(7n+7) {
        grid-column: 6 / span 2;
    }
}

/* Events Section */
.events-group {
    margin-bottom: 4rem;
}

.events-group:last-child {
    margin-bottom: 0;
}

.events-group-title {
    margin-bottom: 2rem;
}

.events-group-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 2rem;
}

.events-group-header .events-group-title {
    margin-bottom: 0;
}

.events-group-header .calendar-subscribe {
    margin: 0;
}

.semester-label {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--accent-red);
    margin: 2.5rem 0 1rem;
}

.semester-label:first-of-type {
    margin-top: 0;
}

.events-note {
    color: var(--text-muted);
}

.past-events {
    opacity: 0.9;
}

.events-list {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.no-events {
    text-align: center;
    padding: 4rem;
    color: var(--text-muted);
}

/* Not Found / Access pages -- the 404 and 403 bands track the hero/page-header
   band exactly: paper sheet in the default light theme, navy Midnight Field in
   dark mode (and ?theme=navy). Base rules below ink for the navy band; the
   light-mode block re-inks type + illustration for the paper sheet. */
.not-found-hero {
    padding: 5rem 0 6rem;
    text-align: center;
    overflow: hidden;
}

.not-found-code {
    font-size: clamp(5rem, 3rem + 8vw, 9rem);
    color: var(--text-on-inverse);
    line-height: 1;
    margin-bottom: 1rem;
}

.not-found-text {
    color: var(--text-on-inverse-muted);
    font-size: var(--text-lg);
    max-width: 46ch;
    margin: 0 auto 2.5rem;
}

.not-found-curve {
    display: block;
    width: min(560px, 100%);
    margin: 0 auto 2.5rem;
}

.not-found-lock {
    display: block;
    width: min(140px, 40%);
    margin: 0 auto 2.5rem;
}

.nf-axis {
    stroke: rgba(255, 255, 255, 0.25);
    stroke-width: 1.5;
    fill: none;
}

.nf-bell {
    stroke: var(--pale-blue);
    stroke-width: 3;
    stroke-linecap: round;
    fill: none;
}

.nf-pointer {
    stroke: rgba(255, 255, 255, 0.4);
    stroke-width: 1.5;
    stroke-dasharray: 3 4;
    fill: none;
}

/* The marker in the far tail (and the padlock keyhole) reads as the descent
   minimum from the loss landscape -- same red as the hero's theta-star. */
.nf-dot {
    fill: var(--red-bright);
}

.nf-label {
    fill: var(--text-on-inverse-muted);
    font-family: var(--font-body);
    font-size: 14px;
    letter-spacing: 0.05em;
}

.not-found-hero .hero-actions {
    justify-content: center;
}

/* Card spec -- every in-flow card on the site (event, center, department)
   follows the same anatomy: --surface fill, 1px --border hairline,
   --border-radius corners, 1.5rem content padding (compact variant:
   1rem 1.15rem), hover = hairline deepens to --border-strong + 2px lift.
   Elevation is hairline + tone, never a drop shadow; shadows belong to
   floating overlays only. */

/* Event Card - accented with the per-type color from events.yaml */
.event-card {
    background: var(--surface);
    border-radius: var(--border-radius);
    display: flex;
    flex-direction: column;
    transition: border-color var(--transition), transform var(--transition);
    border: 1px solid var(--border);
}

.event-card:hover {
    border-color: var(--border-strong);
    transform: translateY(-2px);
}

/* While a card's "Add to calendar" dropdown is open, hold the lifted state
   (regardless of hover) so the card and its menu never shift as the pointer
   moves between them. The transform makes the card a stacking context, so also
   raise it above sibling cards — otherwise a hovered card below (which lifts and
   forms its own stacking context) would paint over the open menu. */
.event-card:has(.event-calendar-menu:not([hidden])) {
    position: relative;
    z-index: 40;
    transform: translateY(-2px);
    border-color: var(--border-strong);
}

.event-card.past {
    opacity: 0.75;
}

/* Metadata row atop the card: quiet mono date on the left, the type pill
   (the card's single colored element) on the right. */
.event-card-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    margin: 1.25rem 1.5rem 0;
}

/* Plain mono date: "JUN 15 MON". The per-type color lives in the pill only. */
.event-date-badge {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.event-day {
    color: var(--text);
}

.event-content {
    /* The metadata row above carries the top offset. */
    padding: 0.9rem 1.5rem 1.5rem;
    flex: 1;
}

.event-header {
    margin-bottom: 0.75rem;
}

/* Type pill tinted by the event color; text stays on the theme token so
   arbitrary YAML hexes never create a contrast problem */
.event-type-pill {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text);
    background: var(--bg-subtle);
    background: color-mix(in srgb, var(--event-color, var(--accent)) 14%, var(--surface));
    border-radius: var(--border-radius);
    padding: 0.2rem 0.6rem;
}

.event-title {
    font-family: var(--font-body);
    font-size: var(--text-lg);
    font-weight: 600;
    margin-bottom: 0;
    color: var(--heading);
}

.event-title a {
    color: var(--heading);
}

.event-title a:hover {
    color: var(--link);
}

.speaker-affiliation {
    color: var(--text-muted);
    font-weight: 400;
    font-size: var(--text-base);
}

.event-talk-title {
    font-size: var(--text-base);
    color: var(--text);
    margin-bottom: 0;
}

.event-meta {
    font-size: 0.9rem;
    color: var(--text-muted);
    margin-bottom: 0.75rem;
}

.event-meta p {
    margin-bottom: 0.25rem;
    color: var(--text-muted);
}

.event-abstract {
    font-size: 0.9rem;
    color: var(--text-muted);
    border-top: 1px solid var(--border);
    padding-top: 0.75rem;
    margin-top: 0.75rem;
}

.event-abstract p {
    color: var(--text-muted);
}

/* On cards, clamp abstracts to a teaser; the detail page has the full text */
.event-card .event-abstract p {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
}

.event-links {
    display: flex;
    align-items: baseline;
    gap: 1rem;
    margin-top: 1rem;
}

.event-link {
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--link);
}

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

.event-link::after {
    content: ' \2192';
}

/* Back variant: leading arrow instead of trailing */
.event-link-back::after {
    content: none;
}

.event-link-back::before {
    content: '\2190 ';
}

/* Per-event "Add to calendar" dropdown */
.event-calendar {
    position: relative;
    display: inline-block;
}

.event-calendar-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--link);
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    transition: color var(--transition);
}

.event-calendar-toggle:hover {
    color: var(--link-hover);
}

/* Caret instead of the trailing arrow the base .event-link carries */
.event-calendar-toggle::after {
    content: '';
    display: inline-block;
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    transition: transform var(--transition);
}

.event-calendar-toggle[aria-expanded="true"]::after {
    transform: rotate(180deg);
}

.event-calendar-menu {
    position: absolute;
    top: calc(100% + 0.4rem);
    left: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    min-width: max-content;
    padding: 0.4rem 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--border-radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

.event-calendar-menu[hidden] {
    display: none;
}

/* Flipped above the toggle when there's no room below (set by JS) */
.event-calendar-menu.drops-up {
    top: auto;
    bottom: calc(100% + 0.4rem);
}

.event-calendar-menu a {
    font: inherit;
    font-size: var(--text-sm);
    text-align: left;
    color: var(--link);
    padding: 0.35rem 1rem;
    white-space: nowrap;
    transition: color var(--transition);
}

.event-calendar-menu a:hover {
    color: var(--link-hover);
}

/* Past events: compact archive rows */
.events-archive {
    margin-bottom: 2.5rem;
}

.event-row {
    display: grid;
    grid-template-columns: 4.5rem 1fr auto;
    column-gap: 1.5rem;
    align-items: baseline;
    padding: 0.85rem 0.75rem;
    border-bottom: 1px solid var(--border);
    transition: background-color var(--transition);
}

.event-row:first-child {
    border-top: 1px solid var(--border);
}

.event-row:hover {
    background: var(--bg-subtle);
}

.event-row-date {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.event-row-speaker {
    display: block;
    font-weight: 600;
    color: var(--heading);
}

.event-row:hover .event-row-speaker {
    color: var(--link);
}

.event-row-affiliation {
    font-weight: 400;
    color: var(--text-muted);
}

.event-row-title {
    display: block;
    font-size: var(--text-sm);
    color: var(--text-muted);
    line-height: 1.5;
    margin-top: 0.1rem;
}

.event-row-type {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-muted);
    white-space: nowrap;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.event-row-type::before {
    content: '';
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--event-color);
    margin-right: 0.45rem;
}

@media (max-width: 600px) {
    .event-row {
        grid-template-columns: 3.5rem 1fr;
    }

    .event-row-type {
        display: none;
    }
}

/* Mailing Lists */
.mailing-lists-section {
    padding: 5rem 0;
}

.mailing-lists-intro {
    color: var(--text-muted);
    margin-bottom: 2.5rem;
}

.mailing-lists {
    max-width: 860px;
}

.mailing-list {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: start;
    gap: 1rem;
    padding: 1.5rem 0.5rem;
    border-bottom: 1px solid var(--border);
}

.mailing-list:first-child {
    border-top: 1px solid var(--border);
}

.mailing-list-icon {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--accent-red);
    /* nudge to optically align with the list name's cap height */
    margin-top: 0.15rem;
}

.mailing-list-icon svg {
    width: 26px;
    height: 26px;
}

.mailing-list-body {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
    min-width: 0;
}

.mailing-list-name {
    font-size: 1.25rem;
    margin-bottom: 0;
}

.mailing-list-description {
    color: var(--text-muted);
    font-size: var(--text-sm);
    margin-bottom: 0;
}

.mailing-list-action {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.mailing-list-note {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.mailing-list-email code {
    font-size: var(--text-sm);
    color: var(--link);
    word-break: break-all;
}

.mailing-list-email:hover code {
    color: var(--link-hover);
}


/* Centers & Institutes */
.centers-section {
    padding: 5rem 0;
}

.centers-intro {
    color: var(--text-muted);
    margin-bottom: 2.5rem;
}

.centers-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1.5rem;
}

.center-card {
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--border-radius);
    overflow: hidden;
    color: var(--text);
    transition: border-color var(--transition), transform var(--transition);
}

.center-card:hover {
    border-color: var(--border-strong);
    transform: translateY(-2px);
    color: var(--text);
}

/* Logos are arbitrary shapes and colors, so keep them contained on a
   fixed white panel that works in both light and dark themes */
.center-logo {
    background: #FFFFFF;
    border-bottom: 1px solid var(--border);
    height: 140px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem;
}

/* For white-on-transparent logos (set logo_background: dark in site.yaml) */
.center-logo-dark {
    background: var(--bg-inverse);
}

.center-logo img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* For photographic banners (set logo_cover: true in site.yaml): fill the
   whole panel edge-to-edge instead of sitting small inside the padding */
.center-logo-cover {
    padding: 0;
}

.center-logo-cover img {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    object-fit: cover;
}

.center-content {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 1.5rem;
    flex: 1;
}

.center-name {
    font-family: var(--font-body);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--heading);
    margin-bottom: 0;
}

.center-full-name {
    font-size: var(--text-sm);
    color: var(--text-muted);
    margin-bottom: 0;
}

.center-description {
    font-size: var(--text-sm);
    margin-bottom: 0;
}

.center-link {
    margin-top: auto;
    padding-top: 0.75rem;
    font-size: var(--text-sm);
    color: var(--link);
    word-break: break-all;
}

.center-card:hover .center-link {
    color: var(--link-hover);
}


/* Get Involved */
.get-involved-section {
    padding: 5rem 0;
}

.get-involved-intro {
    color: var(--text-muted);
    font-size: var(--text-lg);
    max-width: 720px;
    margin-bottom: 2.5rem;
}

/* Each audience path is a stacked block separated by a hairline divider,
   echoing the mailing-list rows. The block spans the container; only the
   prose is held to a readable measure. */
.gi-path {
    padding: 3rem 0;
    border-top: 1px solid var(--border);
    /* Clear the sticky header when reached via a nav dropdown jump link. */
    scroll-margin-top: 90px;
}

.gi-path .section-title {
    margin-bottom: 1rem;
}

.gi-lede {
    color: var(--text-muted);
    max-width: 68ch;
    margin-bottom: 1.75rem;
}

/* Department admissions links: a compact grid of outbound cards. */
.gi-dept-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 0.75rem;
}

.gi-dept {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 1rem 1.15rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--border-radius);
    color: var(--text);
    transition: border-color var(--transition), transform var(--transition);
}

.gi-dept:hover {
    color: var(--text);
    border-color: var(--border-strong);
    transform: translateY(-2px);
}

.gi-dept-name {
    font-weight: 600;
    line-height: 1.3;
}

.gi-dept-link {
    font-size: var(--text-sm);
    color: var(--link);
}

.gi-dept:hover .gi-dept-link {
    color: var(--link-hover);
}

.gi-note {
    margin-top: 1.75rem;
    max-width: 68ch;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

/* Call-to-action buttons on the light page ground (the .btn-* variants are
   tuned for the inverse band, so Get Involved carries its own accent pair). */
.gi-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.gi-btn {
    display: inline-block;
    font-weight: 600;
    font-size: var(--text-base);
    padding: 0.75rem 1.75rem;
    border-radius: var(--border-radius);
    border: 1px solid transparent;
    transition: background-color var(--transition), border-color var(--transition), color var(--transition);
}

.gi-btn-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-contrast);
}

.gi-btn-primary:hover {
    background: var(--link-hover);
    border-color: var(--link-hover);
    color: var(--accent-contrast);
}

.gi-btn-ghost {
    border-color: var(--border);
    color: var(--link);
}

.gi-btn-ghost:hover {
    border-color: var(--link);
    color: var(--link-hover);
}

/* Steering committee contacts in the partners path. */
.gi-directors-heading {
    font-weight: 600;
    color: var(--heading);
    margin-bottom: 1rem;
}

.gi-directors {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem 2.5rem;
}

.gi-director {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.gi-director-photo {
    flex-shrink: 0;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    overflow: hidden;
    background: var(--bg-subtle);
    display: inline-flex;
}

.gi-director-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.gi-director-photo .photo-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    font-family: var(--font-heading);
    font-size: var(--text-xl);
    color: var(--text-muted);
}

.gi-director-info {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

.gi-director-name {
    font-weight: 600;
    color: var(--heading);
}

.gi-director-title {
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.gi-director-email {
    font-size: var(--text-sm);
    color: var(--link);
    word-break: break-all;
}

.gi-director-email:hover {
    color: var(--link-hover);
}

/* Footer */
.site-footer {
    background: var(--bg-inverse);
    color: var(--text-on-inverse);
    padding: 3rem 0;
    margin-top: auto;
}

.footer-content {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 2rem;
}

.footer-info p {
    color: var(--text-on-inverse);
}

.footer-address {
    line-height: 1.8;
    margin-bottom: 0.5rem;
    font-size: 0.9375rem;
}

.footer-email a {
    color: var(--text-on-inverse-muted);
}

.footer-org-link {
    color: var(--text-on-inverse);
    text-decoration: none;
    border-bottom: 1px solid var(--text-on-inverse-muted);
    padding-bottom: 1px;
}

.footer-org-link::after {
    content: " \2197";
    color: var(--text-on-inverse-muted);
}

.footer-org-link:hover {
    color: var(--text-on-inverse);
    border-bottom-color: var(--text-on-inverse);
}

.footer-copyright {
    text-align: right;
    font-size: 0.9375rem;
}

.footer-copyright p {
    margin-bottom: 0.25rem;
    color: var(--text-on-inverse);
}

.build-time {
    font-size: 0.875rem;
    color: var(--text-on-inverse-muted);
}

.build-time a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid var(--text-on-inverse-muted);
    padding-bottom: 1px;
}

.build-time a:hover {
    color: var(--text-on-inverse);
    border-bottom-color: var(--text-on-inverse);
}

/* Responsive Design */

/* Nav collapse gets its own, wider breakpoint than the general mobile layout:
   the full row (logo lockup + five nowrap labels at 24px paddings) needs
   ~890px, so it must fold into the hamburger well before the 768px layout
   shift or the labels would overflow the bar. */
@media (max-width: 940px) {
    .nav-toggle {
        display: block;
    }

    .nav-menu {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        background: var(--nav-bg);
        border-bottom: 1px solid var(--nav-border);
        flex-direction: column;
        padding: 0;
        gap: 0;
    }

    .nav-menu.active {
        display: flex;
    }

    .nav-link {
        padding: 1rem 1.5rem;
        border-top: 1px solid var(--nav-border);
    }

    /* In the stacked menu every item is full-width, so the absolutely
       positioned hover bar would run across the whole row (most visible on
       the People toggle, where iOS keeps :hover applied after the tap).
       Swap it for a plain text underline that hugs the label. */
    .nav-link::after {
        content: none;
    }

    .nav-link:hover,
    .nav-link:focus-visible {
        text-decoration: underline;
        text-decoration-thickness: 2px;
        text-underline-offset: 6px;
    }

    /* The submenus are always expanded in the stacked menu, so the People /
       Get Involved toggles have nothing to do here: make them inert group
       labels instead of buttons that look tappable. */
    .nav-dropdown-toggle {
        pointer-events: none;
        cursor: default;
    }

    .nav-dropdown-toggle:hover,
    .nav-dropdown-toggle:focus-visible {
        text-decoration: none;
    }

    /* In the stacked mobile menu the People submenu is always expanded as an
       indented sublist rather than a floating popover. */
    .nav-dropdown-toggle {
        width: 100%;
        justify-content: flex-start;
    }

    .nav-item-has-children .nav-caret {
        display: none;
    }

    .nav-dropdown-menu {
        position: static;
        opacity: 1;
        visibility: visible;
        min-width: 0;
        padding: 0;
        border-radius: 0;
        box-shadow: none;
        background: transparent;
    }

    .nav-dropdown-link {
        padding: 0.85rem 1.5rem 0.85rem 2.75rem;
        border-top: 1px solid var(--nav-border);
        font-size: 0.9375rem;
    }

    .logo-text {
        font-size: 1rem;
    }
}

@media (max-width: 768px) {
    .hero {
        padding: 4rem 0 4.5rem;
    }

    /* Small screens keep the quiet topography only: the text spans the full
       band width there, so a descent trace would sit behind it. This also
       covers the click easter egg -- launched runs stay invisible. */
    .hero-contours .hc-descent,
    .hero-contours .hc-min,
    .hero-contours .hc-ping,
    .hero-hud {
        display: none;
    }

    /* No display:none for .hero-canvas here: the 3D gate is decided once
       at load (real phones get the SVG path), and a desktop window resized
       below the breakpoint must keep its canvas -- the SVG behind it is
       empty on the 3D path, so hiding the canvas would blank the band. */

    .section,
    .mission-section,
    .people-section,
    .events-section {
        padding: 3.5rem 0;
    }

    /* People filter: swap the wrapping chip row for a compact dropdown so the
       people list stays near the top of the page. */
    .people-filter-chips {
        display: none;
    }

    .people-filter-select-wrap {
        display: block;
    }

    .people-filter {
        margin-bottom: 2.5rem;
        padding-bottom: 1.5rem;
    }

    .stats-row {
        gap: 2rem;
    }

    /* The subscribe panel is right-anchored to its toggle, which on phones
       wraps to the left edge of the events header -- the panel would then
       extend past the left viewport edge. Re-anchor it to the header so it
       drops below the full row and spans its width wherever the toggle sits. */
    .events-group-header {
        position: relative;
    }

    .events-group-header .calendar-subscribe {
        position: static;
    }

    .events-group-header .calendar-subscribe-panel {
        left: 0;
        right: 0;
        min-width: 0;
        max-width: none;
    }

    .events-group-header .calendar-subscribe-links {
        white-space: normal;
    }

    .footer-content {
        flex-direction: column;
        gap: 1.5rem;
    }

    .footer-copyright {
        text-align: left;
    }
}

@media (max-width: 480px) {
    .container {
        padding: 0 16px;
    }

    .photo-row {
        padding-left: 0;
    }

    .photo-row + .photo-row {
        margin-top: -6px;
        transform: translateX(18px);
    }

    .strip-face {
        width: 44px;
        height: 44px;
        margin-left: -8px;
    }

    .photo-row .strip-face:first-child {
        margin-left: 0;
    }

    .stats-row {
        gap: 1.5rem;
    }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
    }

    .event-card:hover,
    .photo-strip:hover .strip-face,
    .person-tile:hover .tile-photo img,
    .person-tile:hover .tile-photo .photo-placeholder {
        transform: none;
    }
}

/* Light-mode band: defaults to the "paper" light ground; ?theme=<wash|white>
   swaps the gradient and ?theme=navy restores the old Midnight Field band
   (the main.js flag sets data-theme on <html>; sticks per tab). Light mode
   only -- dark always keeps the navy band regardless of the flag. The
   ink/type/red swaps apply to every light band EXCEPT navy; only the band
   gradient differs between paper/wash/white. Once wash/white/navy are ruled
   out, drop the flag machinery (main.js IIFE + the non-default rules here)
   and keep just the shared swaps + the paper gradient. */
@media (prefers-color-scheme: light) {
    /* On the light ground the band type follows the page hierarchy: ink
       titles, muted body, red eyebrows (red text lives on light grounds; on
       the navy band it stays muted blue). Applies to the default paper band
       and to wash/white, but not when navy is explicitly selected. */
    :root:not([data-theme="navy"]) .hero-title,
    :root:not([data-theme="navy"]) .page-title,
    :root:not([data-theme="navy"]) .not-found-code {
        color: var(--heading);
    }

    :root:not([data-theme="navy"]) .hero-tagline,
    :root:not([data-theme="navy"]) .page-subtitle,
    :root:not([data-theme="navy"]) .not-found-text {
        color: var(--text-muted);
    }

    :root:not([data-theme="navy"]) .hero-eyebrow,
    :root:not([data-theme="navy"]) .page-header .section-eyebrow {
        color: var(--accent-red);
    }

    /* Contours ink in Columbia blue; the descent graphic drops from the
       navy-legible --red-bright to the deep brand red. The 404/403 illustration
       (bell curve, padlock) shares this ink: distribution curve in Columbia
       blue, the tail marker / keyhole in the descent red. */
    :root:not([data-theme="navy"]) .hc-ring,
    :root:not([data-theme="navy"]) .nf-bell {
        stroke: #2A79C0;
    }

    :root:not([data-theme="navy"]) .hc-path,
    :root:not([data-theme="navy"]) .hc-ping {
        stroke: #A6192E;
    }

    :root:not([data-theme="navy"]) .hc-min,
    :root:not([data-theme="navy"]) .nf-dot {
        fill: #A6192E;
    }

    /* Axis, pointer, and label were white for the navy band; on paper they
       read as light ink. */
    :root:not([data-theme="navy"]) .nf-axis {
        stroke: color-mix(in srgb, var(--text) 22%, transparent);
    }

    :root:not([data-theme="navy"]) .nf-pointer {
        stroke: color-mix(in srgb, var(--text) 32%, transparent);
    }

    :root:not([data-theme="navy"]) .nf-label {
        fill: var(--text-muted);
    }

    /* Header topography needs more presence on a light sheet than the
       0.14/0.26 tuned for navy. */
    :root:not([data-theme="navy"]) .header-contours .hc-ring {
        opacity: 0.25;
    }

    :root:not([data-theme="navy"]) .header-contours .hc-ring-inner {
        opacity: 0.45;
    }

    :root:not([data-theme="navy"]) .hero-hud {
        color: var(--text-muted);
    }

    :root:not([data-theme="navy"]) .hero-hud-control input[type="range"]::-webkit-slider-runnable-track {
        background: color-mix(in srgb, #2A79C0 45%, transparent);
    }

    :root:not([data-theme="navy"]) .hero-hud-control input[type="range"]::-webkit-slider-thumb {
        background: #2A79C0;
    }

    :root:not([data-theme="navy"]) .hero-hud-control input[type="range"]::-moz-range-track {
        background: color-mix(in srgb, #2A79C0 45%, transparent);
    }

    :root:not([data-theme="navy"]) .hero-hud-control input[type="range"]::-moz-range-thumb {
        background: #2A79C0;
    }

    /* Default band = paper: the cool map-paper sheet -- white key light
       top-right, cool gray pool bottom-left. Overrides the base navy band
       (later, equal specificity); wash/white/navy below override this. */
    .hero,
    .page-header,
    .not-found-hero {
        background-image:
            var(--grain),
            radial-gradient(58% 88% at 90% 6%, rgba(255, 255, 255, 0.75) 0%, transparent 60%),
            radial-gradient(56% 84% at 3% 105%, rgba(150, 168, 175, 0.10) 0%, transparent 60%),
            linear-gradient(135deg, #EEF1EF 0%, #E4E9E6 100%);
    }

    /* wash: pale Columbia-blue tint extending the navbar's family downward. */
    :root[data-theme="wash"] .hero,
    :root[data-theme="wash"] .page-header,
    :root[data-theme="wash"] .not-found-hero {
        background-image:
            var(--grain),
            radial-gradient(58% 88% at 90% 6%, rgba(255, 255, 255, 0.8) 0%, transparent 60%),
            radial-gradient(56% 84% at 3% 105%, color-mix(in srgb, var(--pale-blue) 8%, transparent) 0%, transparent 60%),
            linear-gradient(135deg, #E3EEF7 0%, #CCE0F0 100%);
    }

    /* white: band nearly merges with the page; faint pale-blue corner
       glows are the only tint, the contours carry the band. */
    :root[data-theme="white"] .hero,
    :root[data-theme="white"] .page-header,
    :root[data-theme="white"] .not-found-hero {
        background-image:
            var(--grain),
            radial-gradient(58% 88% at 90% 6%, color-mix(in srgb, var(--pale-blue) 10%, transparent) 0%, transparent 60%),
            radial-gradient(56% 84% at 3% 105%, color-mix(in srgb, var(--pale-blue) 7%, transparent) 0%, transparent 60%),
            linear-gradient(135deg, #FFFFFF 0%, #EFF4F8 100%);
    }

    /* navy: restore the old Midnight Field band (matches the base .hero rule
       above, re-declared here so the paper default doesn't win). The shared
       ink/type swaps are already excluded for navy, so white type and
       pale-blue contours come back with it. */
    :root[data-theme="navy"] .hero,
    :root[data-theme="navy"] .page-header,
    :root[data-theme="navy"] .not-found-hero {
        background-image:
            var(--grain),
            radial-gradient(58% 88% at 90% 6%, color-mix(in srgb, var(--pale-blue) 18%, transparent) 0%, transparent 60%),
            radial-gradient(56% 84% at 3% 105%, color-mix(in srgb, var(--pale-blue) 12%, transparent) 0%, transparent 60%),
            linear-gradient(135deg, #0A1F3D 0%, #05132B 100%);
    }

    /* Footer echoes the paper hero band, bookending the page in one material
       now that the bands are no longer navy. Every footer text rule derives
       from these two on-inverse tokens, so remapping them here flips all of
       the footer copy to page ink at once; links go blue like the rest of the
       site. ?theme=navy is excluded, so the navy footer returns with the navy
       bands. Dark mode is untouched -- its bands stay navy, so the navy footer
       stays with them. */
    :root:not([data-theme="navy"]) .site-footer {
        background-image:
            var(--grain),
            linear-gradient(135deg, #EEF1EF 0%, #E4E9E6 100%);
        background-color: #E4E9E6;
        border-top: 1px solid var(--border);
        --text-on-inverse: var(--text);
        --text-on-inverse-muted: var(--text-muted);
    }

    :root:not([data-theme="navy"]) .footer-email a,
    :root:not([data-theme="navy"]) .footer-org-link,
    :root:not([data-theme="navy"]) .build-time a {
        color: var(--link);
    }
}
