/* ==========================================================================
   40-INTERACTIVE.CSS — states that only exist once JavaScript is running.
   ==========================================================================
   Everything here is additive. If the JS never loads, no rule in this file
   applies and the page remains fully usable: the drawer is unopened, the hero
   shows its first slide, the gallery scrolls natively, and gallery images are
   ordinary links to their full-size files.
   ========================================================================== */

/* Set by navigation.js while the drawer is open. Locks background scroll
   without collapsing scroll position. */
.tdc-scroll-locked {
  overflow: hidden;
}

/* Progressive-enhancement gates. Components add .is-enhanced only after their
   module has initialised successfully. */
.c-hero:not(.is-enhanced) .c-hero__slide[hidden] {
  /* Belt-and-braces: if [hidden] was authored server-side but the carousel
     never initialised, force every slide back into view rather than hiding
     content permanently. */
  display: grid !important;
}

/* ============================== LIGHTBOX ================================ */
.c-lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--z-lightbox);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-lg);
  background-color: rgba(3, 25, 50, 0.92);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--transition-base),
    visibility var(--transition-base);
}

.c-lightbox.is-open {
  opacity: 1;
  visibility: visible;
}

.c-lightbox__figure {
  max-width: min(100%, 1100px);
  margin: 0;
}

.c-lightbox__image {
  max-height: 80vh;
  width: auto;
  border-radius: var(--radius-sm);
}

.c-lightbox__caption {
  margin-block-start: var(--space-sm);
  color: var(--color-text-on-dark-muted);
  font-size: var(--text-sm);
  text-align: center;
}

.c-lightbox__close {
  position: absolute;
  top: var(--space-lg);
  right: var(--space-lg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap-target);
  min-height: var(--tap-target);
  border: 1px solid var(--color-border-on-dark);
  border-radius: var(--radius-full);
  background-color: transparent;
  color: var(--color-text-on-dark);
  cursor: pointer;
}

.c-lightbox__close:focus-visible {
  outline-color: var(--focus-color-on-dark);
}

/* Scroll-driven reveal. Deliberately minimal: opacity and a small translate,
   nothing that moves layout. Elements are visible by default so content is
   never dependent on the observer firing. */
.js-reveal {
  opacity: 1;
}

.tdc-js .js-reveal {
  opacity: 0;
  transform: translateY(var(--space-sm));
  transition:
    opacity var(--duration-slow) var(--ease-out),
    transform var(--duration-slow) var(--ease-out);
}

.tdc-js .js-reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .tdc-js .js-reveal {
    opacity: 1;
    transform: none;
  }
}

/* ============================== NOTICE TICKER ==========================
   Added by notice-ticker.js only when all of these hold: the script loaded,
   the visitor has not asked for reduced motion, and the notice list is
   actually taller than the moving window. Everything else keeps the static
   full-height list in 30-components.css.

   On the desktop people split the window is not a fixed cap: it is the
   height the faculty column leaves beside the notices head, so the notices
   run to the band's bottom with no dead space below them. The track is
   taken out of flow for that — an in-flow track would let the full list
   grow the very column the window is measured against, and the list would
   always "fit" and never move. Out of flow, the column bottoms out at its
   head, the band row is sized by the faculty side alone, and the viewport
   (flex: 1 in 30-components.css) stretches to exactly the leftover. No
   pixel constants, and the window re-fills itself if the faculty content
   ever changes height. */

.l-split--people .c-notices.is-enhanced .c-notices__viewport {
  position: relative;
  min-height: 0;
  overflow: hidden;
}

.l-split--people .c-notices.is-enhanced .c-notices__track {
  position: absolute;
  inset-inline: 0;
  top: 0;
}

/* Stacked layout (below 1024): the columns collapse, so there is no
   faculty column to size the window against — the fixed cap from
   30-components.css becomes the window's height instead. The track stays
   out of flow (rule above), so the cap has to arrive as a height, not a
   max-height, or the box would collapse to nothing. */
@media (max-width: 1023px) {
  .c-notices.is-enhanced .c-notices__viewport {
    height: var(--tdc-notices-max, 17.5rem);
  }
}

/* The track is the notice list twice (the script's copy is aria-hidden);
   translateY(-50%) advances exactly one copy, so the loop point falls on
   identical content and the movement never jumps. The duration is measured
   per content height by the script and arrives as a custom property. */
.c-notices.is-enhanced .c-notices__track {
  animation: tdc-notices-scroll var(--tdc-notices-duration, 40s) linear infinite;
}

/* Any attention pauses the movement (WCAG 2.2.2): the mouse over the list,
   or focus arriving anywhere inside it. The script additionally holds the
   track still and slides it so a focused notice is on screen. */
.c-notices.is-enhanced:hover .c-notices__track,
.c-notices.is-enhanced:focus-within .c-notices__track {
  animation-play-state: paused;
}

@keyframes tdc-notices-scroll {
  from {
    transform: translateY(0);
  }

  to {
    transform: translateY(-50%);
  }
}

/* Belt and braces: the script never enhances under reduced motion, and this
   guarantees the list cannot move even if the preference arrives after
   load. The static full-height list is the presentation, not a fallback.
   The is-enhanced selectors are repeated here because the fill rules above
   are MORE specific — without the repetition they would win and the list
   would stay clipped and out of flow while its content overflowed
   visibly. Everything the enhancement did to the geometry is undone:
   back in flow, back to content height, nothing clipped. */
@media (prefers-reduced-motion: reduce) {
  .c-notices .c-notices__track,
  .l-split--people .c-notices.is-enhanced .c-notices__track {
    animation: none;
    position: static;
    transform: none;
  }

  .c-notices .c-notices__viewport,
  .l-split--people .c-notices.is-enhanced .c-notices__viewport {
    height: auto;
    max-height: none;
    min-height: auto;
    overflow: visible;
  }
}
