/* Shared project-page chrome for xuanyouliu.com paper pages */

@font-face {
  font-family: "Merriweather";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../Asset/fonts/merriweather-latin.woff2?v=2") format("woff2");
}

@font-face {
  font-family: "Merriweather";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../Asset/fonts/merriweather-latin-italic.woff2?v=2") format("woff2");
}

:root {
  --color-bg: #fafafa;
  --color-bg-alt: #f5f5f5;
  --color-surface: #ffffff;
  --color-text: #1a1a1a;
  --color-text-muted: #555555;
  --color-text-light: #888888;
  --color-accent: #4e2a84;
  --color-accent-hover: #3d2069;
  --color-accent-light: #e8e0f0;
  --color-border: #e0e0e0;
  --color-border-light: #eeeeee;
  --color-on-accent: #ffffff;
  --nav-bg: rgba(250, 250, 250, 0.95);
  --font-serif: "Merriweather", Georgia, "Times New Roman", serif;
  --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue",
    "Segoe UI", sans-serif;
  --measure: 42rem;
  /* Match homepage: --max-width + section horizontal padding (space-lg) */
  --page-pad: 1.5rem;
  --hero-max: min(92vw, 1320px);
  /* Story pages cap the content column so figures stay a readable size. */
  --story-max: min(92vw, 60rem);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #121212;
    --color-bg-alt: #1a1a1a;
    --color-surface: #242424;
    --color-text: #e4e4e4;
    --color-text-muted: #a0a0a0;
    --color-text-light: #707070;
    --color-accent: #b39ddb;
    --color-accent-hover: #d1c4e9;
    --color-accent-light: #3d2069;
    --color-border: #333333;
    --color-border-light: #2a2a2a;
    --color-on-accent: #121212;
    --nav-bg: rgba(18, 18, 18, 0.95);
  }
}

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

html {
  /* Keeps anchor targets clear of the sticky topbar. project.js measures the
     real bar height into --nav-scroll-offset; the fallback covers no-JS. */
  scroll-padding-top: var(--nav-scroll-offset, 4.5rem);
}

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

body.project-page {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

.skip-link {
  position: absolute;
  top: -100%;
  left: 50%;
  transform: translateX(-50%);
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  padding: 0.5rem 0.85rem;
  border-radius: 6px;
  z-index: 10000;
  font-weight: 500;
  text-decoration: none;
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: 0.75rem;
  color: var(--color-on-accent);
  outline: none;
}

img,
video {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--color-accent);
  text-underline-offset: 2px;
}

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

/* Full-bleed sticky bar (like homepage `.nav`); the inner row carries the
   content measure so the blur/border run edge-to-edge on wide screens. */
.project-topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  box-sizing: border-box;
  width: 100%;
  background: var(--nav-bg);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--color-border-light);
}

.project-topbar-inner {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 1rem;
  box-sizing: border-box;
  width: 100%;
  max-width: var(--hero-max);
  margin: 0 auto;
  padding: 0.75rem var(--page-pad);
}

/* Match the homepage `.nav-logo` weight and tracking so the sticky bar
   reads as the same chrome, not a second brand system. */
.project-brand {
  font-family: var(--font-serif);
  font-weight: 900;
  font-size: 1.15rem;
  letter-spacing: -0.04em;
  color: var(--color-text);
  text-decoration: none;
  transition: color 0.3s ease;
  flex-shrink: 0;
}

.project-brand:hover {
  color: var(--color-accent);
}

/* Narrow screens swap the full name for the homepage "XL" mark. */
.project-brand-short {
  display: none;
}

.project-topnav {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.75rem 1.5rem;
  /* Pushes section links toward the right-side controls. */
  margin-left: auto;
}

/* Present + Home + (mobile) hamburger sit as one control cluster, matching
   the homepage `.nav-controls` packing. */
.project-topbar-controls {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
}

/* Exact copy of homepage `.nav-toggle` (styles.css) — keep in sync. */
.project-nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  min-width: 44px;
  min-height: 44px;
  padding: 0.25rem;
  margin-right: -8px;
  color: var(--color-text);
}

.project-nav-toggle span {
  display: block;
  width: 24px;
  height: 2px;
  background-color: var(--color-text);
  transition: transform 150ms ease;
}

.project-nav-toggle.active span:first-child {
  transform: rotate(45deg) translate(5px, 5px);
}

.project-nav-toggle.active span:last-child {
  transform: rotate(-45deg) translate(5px, -5px);
}

/* Section links: same muted type + underline sweep as homepage `.nav-link`.
   nowrap + no shrink so the row never wraps; JS folds to the hamburger when
   that single line would overflow the topbar. */
.project-topnav a {
  position: relative;
  flex-shrink: 0;
  white-space: nowrap;
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--color-text-muted);
  text-decoration: none;
}

.project-topnav a::after {
  content: "";
  position: absolute;
  bottom: -4px;
  left: 0;
  width: 0;
  height: 1px;
  background-color: var(--color-accent);
  transition: width 0.2s ease;
}

@media (hover: hover) and (pointer: fine) {
  .project-topnav a:hover {
    color: var(--color-text);
  }

  .project-topnav a:hover::after {
    width: 100%;
  }
}

/* Scroll-spy: project.js marks the section currently on screen. */
.project-topnav a.active {
  color: var(--color-accent);
  font-weight: 600;
}

.project-topnav a.active::after {
  width: 100%;
  height: 3px;
}

.project-topnav a:focus-visible,
.project-link:focus-visible,
.project-brand:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

/* Home is the primary escape hatch back to the site — filled accent so it
   reads clearly against Present (outline) and the hamburger. */
.project-home {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
  padding: 0.4rem 0.8rem;
  border-radius: 999px;
  border: 1px solid var(--color-accent);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font-size: 0.85rem;
  font-weight: 550;
  text-decoration: none;
  transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease,
    transform 0.15s ease;
}

@media (hover: hover) and (pointer: fine) {
  .project-home:hover {
    border-color: var(--color-accent-hover);
    color: var(--color-on-accent);
    background: var(--color-accent-hover);
    transform: translateY(-1px);
  }
}

.project-home:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.project-badge {
  display: inline-block;
  margin: 0 0 0.65rem;
  padding: 0.2rem 0.55rem;
  border-radius: 3px;
  background: var(--color-accent-light);
  color: var(--color-accent);
  font-size: 0.72rem;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.project-authors {
  margin: 0 0 1rem;
  color: var(--color-text-muted);
  font-size: 0.98rem;
}

.project-authors strong {
  color: var(--color-text);
  font-weight: 650;
}

.project-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.55rem;
}

.project-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.45rem 0.8rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 0.88rem;
  font-weight: 550;
  text-decoration: none;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.project-link:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

.project-link.primary {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}

.project-link.primary:hover {
  background: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
  color: var(--color-on-accent);
}

.project-main {
  max-width: var(--hero-max);
  margin: 0 auto;
  padding: 1.5rem var(--page-pad) 3.5rem;
}

.project-section {
  padding: 2.25rem 0;
  border-top: 1px solid var(--color-border-light);
}

.project-section:first-of-type {
  border-top: none;
  padding-top: 1.25rem;
}

.project-section h2 {
  margin: 0 0 0.85rem;
  max-width: 28ch;
  font-family: var(--font-serif);
  font-size: clamp(1.35rem, 2.6vw, 1.7rem);
  line-height: 1.25;
  letter-spacing: -0.015em;
}

.project-section > p,
.project-section > ul {
  max-width: var(--measure);
}

.project-section p {
  margin: 0 0 0.95rem;
  color: var(--color-text);
}

.project-section p.lead {
  font-size: 1.05rem;
  color: var(--color-text-muted);
}

.project-section ul {
  margin: 0 0 1rem;
  padding-left: 1.2rem;
  color: var(--color-text);
}

.project-section li + li {
  margin-top: 0.35rem;
}

.project-metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: 0.75rem;
  margin: 1.1rem 0 1.35rem;
  max-width: 40rem;
}

.project-metric {
  padding: 0.85rem 0.95rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border-light);
  border-radius: 4px;
}

.project-metric strong {
  display: block;
  font-family: var(--font-serif);
  font-size: 1.35rem;
  letter-spacing: -0.02em;
  color: var(--color-accent);
  line-height: 1.15;
}

.project-metric span {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.8rem;
  line-height: 1.35;
  color: var(--color-text-muted);
}

.project-footer {
  border-top: 1px solid var(--color-border);
  background: var(--color-bg-alt);
  padding: 2rem var(--page-pad) 3rem;
}

.project-footer-inner {
  max-width: var(--hero-max);
  margin: 0 auto;
}

.project-footer h2 {
  margin: 0 0 0.75rem;
  font-family: var(--font-serif);
  font-size: 1.15rem;
}

.project-bibtex-wrap {
  position: relative;
  margin: 0 0 1.25rem;
}

.project-bibtex {
  margin: 0;
  padding: 1rem 2.75rem 1rem 1.1rem;
  overflow-x: hidden;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: 0.78rem;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.project-copy-bib {
  appearance: none;
  position: absolute;
  right: 0.45rem;
  bottom: 0.45rem;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  margin: 0;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-surface);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}

.project-copy-bib:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
  background: var(--color-bg-alt);
}

.project-copy-bib:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.project-copy-bib svg {
  width: 14px;
  height: 14px;
}

.project-copy-bib .icon-check {
  display: none;
}

.project-copy-bib.is-copied {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

.project-copy-bib.is-copied .icon-copy {
  display: none;
}

.project-copy-bib.is-copied .icon-check {
  display: block;
}

/* Story layout: teaser image first, title below (Seeing with the Hands content; site chrome) */
.project-page--story {
  background: var(--color-bg);
}

.project-page--story .project-topbar-inner {
  max-width: var(--story-max);
}

.project-titleblock {
  max-width: var(--story-max);
  margin: 0 auto;
  padding: clamp(1.25rem, 3vw, 2rem) var(--page-pad) 0.25rem;
  text-align: left;
}

/* Split hero: title column beside a three-image collage. */
/* align-items: stretch (the default) makes both columns exactly the row
   height, so the collage bottom lines up with the text bottom. */
/* Badge + title run full width on their own row; everything else sits beside
   the hero image below. Lifting the title out of the narrow column is what
   lets the image hold a true 1.5:1 while both columns still end flush. */
.project-titleblock--split {
  display: block;
  padding-top: clamp(1.5rem, 3.5vw, 2.75rem);
}

.project-titleblock-lead {
  margin-bottom: clamp(1.25rem, 2.5vw, 1.85rem);
}

.project-titleblock-row {
  display: grid;
  grid-template-columns: 0.5fr 1fr;
  gap: 2rem 2.5rem;
  align-items: stretch;
}

/* Below ~1000px the split would cramp the text column, so the hero image is
   hidden entirely and the title block runs full width, same as on phones.
   The image is loading="lazy", so display:none also stops it being fetched. */
@media (max-width: 1000px) and (min-width: 721px) {
  .project-titleblock-row {
    grid-template-columns: 1fr;
  }

  .project-hero-figure {
    display: none;
  }
}

/* On narrow screens the title block stays compact text: the venue banner is
   dropped along with the hero. Banners are loading="lazy" too, so hiding them
   also skips the download. */
@media (max-width: 1000px) {
  .project-venue {
    display: none;
  }
}

/* Badge sits flush with the top of the hero image and the venue banner flush
   with its bottom, so the two columns line up on both edges. space-between
   spreads the slack across two gaps instead of pooling it above the banner. */
.project-titleblock-text {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 1.25rem;
}

/* Flex stretch would blow the pill out to the full column width. */
.project-titleblock--split .project-badge {
  align-self: flex-start;
}

.project-venue {
  margin: 0;
}

/* No max-width: the banner always spans the full text column, matching the
   title and author lines above it at every viewport. */
.project-venue img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
}

/* Single composite hero image, always shown whole: the img keeps its
   intrinsic aspect ratio and is never cropped. align-self:start stops the
   grid from stretching the figure to the row height, so a text column taller
   than the image leaves space below the image instead of zooming into it. */
.project-hero-figure {
  margin: 0;
  align-self: start;
  /* Without min-width:0 a grid item's automatic minimum size is the image's
     intrinsic width, which inflates this column and squeezes the title column. */
  min-width: 0;
  max-width: 100%;
}

.project-hero-figure img {
  width: 100%;
  height: auto;
  border: 1px solid var(--color-border-light);
  border-radius: 4px;
  background: var(--color-bg-alt);
}

.project-titleblock h1 {
  margin: 0 0 0.35rem;
  font-family: var(--font-serif);
  font-size: clamp(2rem, 5vw, 2.85rem);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.025em;
  text-wrap: balance;
  color: var(--color-text);
}

.project-subtitle {
  margin: 0 0 1rem;
  font-size: clamp(1.05rem, 2.2vw, 1.25rem);
  line-height: 1.35;
  color: var(--color-text-muted);
  font-weight: 450;
}

/* The title spans the full page width here rather than a narrow column, so it
   can run much larger and still sit on one line. Must come after the base
   rules above: `.project-titleblock--split h1` has equal specificity to
   `.project-titleblock h1`, so source order decides the winner. */
.project-titleblock--split h1 {
  font-size: clamp(2.2rem, 6.4vw, 4.2rem);
  line-height: 1.05;
  margin-bottom: 0;
}

.project-titleblock--split .project-subtitle {
  font-size: clamp(1.1rem, 2.5vw, 1.4rem);
  line-height: 1.4;
}

.project-titleblock .project-authors {
  margin-bottom: 1.15rem;
}

/* Inside the split title column, spacing comes from the flex gap alone.
   Zeroing per-element margins here keeps the rhythm even; mixed margins on
   top of the gap made some gaps twice the size of others. Must come after
   the margin rules above so it wins the source-order tie. */
.project-titleblock--split .project-titleblock-text > * {
  margin: 0;
}

.project-equal {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.82rem;
  color: var(--color-text-light);
}

.project-page--story .project-main {
  max-width: var(--story-max);
  padding-top: 0.25rem;
}

/* No measure cap here: section headings are short enough to sit on one line at
   the story column width, and wrapping them reads as an accident. */
.project-page--story .project-section h2 {
  max-width: none;
  text-wrap: balance;
  color: var(--color-text);
}

/* Body text runs the full story column width. */
.project-page--story .project-section > p,
.project-page--story .project-section > ul,
.project-page--story .project-section > .lead {
  max-width: none;
}

.project-page--story .project-section p.lead {
  font-size: 1.12rem;
  line-height: 1.65;
  color: var(--color-text);
}

.project-page--story .project-section strong {
  color: var(--color-text);
  font-weight: 650;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.project-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.75rem 1.75rem;
  margin-top: 1.25rem;
}

/* Single full-width figure: for a wide panel (e.g. a row of matrices) that
   would be unreadably small inside a two-up grid. */
.project-figure {
  margin: 2rem 0 0;
}

.project-figure img {
  width: 100%;
  border: 1px solid var(--color-border-light);
  border-radius: 4px;
  background: var(--color-bg-alt);
}

.project-figure figcaption {
  margin-top: 0.7rem;
  padding-left: 0.75rem;
  border-left: 2px solid var(--color-border);
  font-size: 0.845rem;
  line-height: 1.55;
  color: var(--color-text-muted);
}

body.present-mode .slide-active .project-figure {
  margin-top: 0;
}

body.present-mode .slide-active .project-figure img {
  max-height: 34vh;
  width: auto;
  max-width: 100%;
  margin-inline: auto;
}

/* Same rule as the two-up figures: the caption must not outrun its picture. */
body.present-mode .slide-active .project-figure {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

body.present-mode .slide-active .project-figure > figcaption {
  width: 0;
  min-width: 100%;
}

/* Prose following a figure grid needs clear separation from the captions
   above it, which sit close to figure-caption size. */
.project-cols + p,
.project-cols + ul {
  margin-top: 2rem;
}

/* Figure-beside-text rows: the figure column runs wider than the prose column
   so the image height roughly matches the text block, and the figure centers
   vertically so any remaining difference splits evenly above and below.
   The balance is tuned for the capped content width (--story-max), which is
   constant above 1000px; below that the columns get too narrow for prose, so
   the row stacks media-over-text instead (same breakpoint as the hero). */
.project-cols--figtext {
  grid-template-columns: 1.3fr 1fr;
  align-items: center;
}

@media (max-width: 1000px) {
  .project-cols--figtext {
    grid-template-columns: 1fr;
    align-items: start;
  }
}

/* Three-up variant for sections that split into three parallel parts. Drops
   straight to one column at 900px -- three ~200px columns are harder to read
   than a stack, so it collapses earlier than the two-up default. */
.project-cols--three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.5rem 1.5rem;
}

/* Lock every panel to the same box so mixed sources (photo, collage, chart)
   read as one row of equal frames rather than three drifting aspect ratios. */
.project-cols--three .project-col > img,
.project-cols--three .project-col > video {
  aspect-ratio: 4 / 3;
  object-fit: contain;
}

@media (max-width: 900px) {
  .project-cols--three {
    grid-template-columns: 1fr;
  }
}

.project-col {
  margin: 0;
}

.project-col img,
.project-col video {
  width: 100%;
  border: 1px solid var(--color-border-light);
  border-radius: 4px;
  background: var(--color-bg-alt);
}

/* Captions are supporting text, not body copy: smaller, quieter, and hung off
   a rule so the eye reads figure -> caption -> next figure. */
.project-col figcaption {
  margin-top: 0.7rem;
  padding-left: 0.75rem;
  border-left: 2px solid var(--color-border);
  font-size: 0.845rem;
  line-height: 1.55;
  color: var(--color-text-muted);
}

/* auto-fit lets the row hold however many members a page has: 4 on Seeing,
   6 on MARIO, one balanced row either way. */
.project-team {
  list-style: none;
  margin: 1.25rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
  gap: 0.75rem;
}

/* The prose-list rule `.project-section li + li` adds 0.35rem margin-top,
   which pushed every member after the first 5.6px lower in the grid row.
   Grid gap handles spacing here. */
.project-team li + li {
  margin-top: 0;
}

.project-team a {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  text-align: center;
  text-decoration: none;
  color: var(--color-text-muted);
  font-size: 0.9rem;
}

.project-team a:hover {
  color: var(--color-accent);
}

.project-team img {
  width: 5.5rem;
  height: 5.5rem;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--color-border);
  background: var(--color-bg-alt);
}

.project-team strong {
  color: var(--color-text);
}

.project-reference {
  margin: 1rem 0 1.25rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--color-border-light);
  border-radius: 4px;
  background: var(--color-bg-alt);
  color: var(--color-text-muted);
  font-size: 0.92rem;
  line-height: 1.5;
}

.project-funding-logo {
  margin-top: 0.75rem;
}

.project-funding-logo img {
  max-width: min(20rem, 100%);
  height: auto;
}

.project-foot-note {
  margin: 0 0 1rem;
  color: var(--color-text-light);
  font-size: 0.9rem;
  line-height: 1.5;
}

.project-page--story .project-footer-inner {
  max-width: var(--story-max);
}

/* Floating back-to-top, matching the homepage control (styles.css .back-to-top) */
.back-to-top {
  position: fixed;
  bottom: 30px;
  right: 30px;
  width: 44px;
  height: 44px;
  padding: 0;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  opacity: 0;
  visibility: hidden;
  transform: translateY(20px);
  transition: opacity 250ms ease, visibility 250ms ease, transform 250ms ease,
    background-color 250ms ease, border-color 250ms ease, color 250ms ease;
  z-index: 999;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.back-to-top.visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

@media (hover: hover) and (pointer: fine) {
  .back-to-top:hover {
    background-color: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-on-accent);
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
  }
}

.back-to-top:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

@media (max-width: 768px) {
  .back-to-top {
    bottom: 20px;
    right: 20px;
    /* Maintain WCAG 2.5.5 minimum 44x44 touch target. */
    width: 44px;
    height: 44px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .back-to-top {
    transition: opacity 150ms ease, visibility 150ms ease;
    transform: none;
  }

  .back-to-top.visible,
  .back-to-top:hover {
    transform: none;
  }
}

/* ============================================
   ANIMATIONS
   Mirrors the homepage system (styles.css): the same 18px rise, 0.36s
   ease-out, and .animate-on-scroll / .is-visible contract.
   ============================================ */

@keyframes projectFadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.project-page--story .project-titleblock-lead,
.project-page--story .project-titleblock-text {
  animation: projectFadeInUp 0.8s ease-out both;
}

.project-page--story .project-hero-figure {
  animation: projectFadeInUp 0.8s ease-out both;
  animation-delay: 0.12s;
}

.animate-on-scroll {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.36s ease-out, transform 0.36s ease-out;
}

.animate-on-scroll.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Columns within a section rise in sequence, like the publication list. */
.project-cols > .animate-on-scroll:nth-child(2) {
  transition-delay: 0.08s;
}

.project-team > li.animate-on-scroll:nth-child(1) { transition-delay: 0s; }
.project-team > li.animate-on-scroll:nth-child(2) { transition-delay: 0.05s; }
.project-team > li.animate-on-scroll:nth-child(3) { transition-delay: 0.09s; }
.project-team > li.animate-on-scroll:nth-child(4) { transition-delay: 0.13s; }

/* Figures lift slightly on hover, echoing the homepage cards. */
.project-col img,
.project-hero-figure img {
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}

@media (hover: hover) {
  .project-hero-figure img:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  }
}

@media (hover: hover) {
  .project-col:hover img {
    transform: translateY(-3px);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  }
}

.project-team img {
  transition: transform 0.25s ease, border-color 0.25s ease;
}

@media (hover: hover) {
  .project-team a:hover img {
    transform: scale(1.06);
    border-color: var(--color-accent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .project-page--story .project-titleblock-lead,
  .project-page--story .project-titleblock-text,
  .project-page--story .project-hero-figure {
    animation: none;
  }

  .animate-on-scroll {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0s !important;
  }

  .project-col img,
  .project-hero-figure img,
  .project-team img {
    transition: none;
  }

  .project-col:hover img,
  .project-hero-figure img:hover,
  .project-team a:hover img {
    transform: none;
    box-shadow: none;
  }
}

@media (max-width: 720px) {
  body.project-page {
    font-size: 16px;
  }

  /* Stack overlay heroes so long paper titles are not clipped */
  .project-hero {
    display: flex;
    flex-direction: column;
    overflow: visible;
  }

  .project-hero-media {
    max-height: 42vh;
    flex-shrink: 0;
  }

  .project-hero-scrim {
    position: relative;
    inset: auto;
    padding: 1.15rem var(--page-pad) 1.35rem;
    background: var(--color-bg);
  }

  .project-hero h1 {
    max-width: none;
    font-size: clamp(1.35rem, 6.2vw, 1.85rem);
  }

  .project-titleblock h1 {
    font-size: clamp(1.7rem, 7vw, 2.2rem);
  }

  body.project-page {
    --page-pad: 1.5rem;
  }

  .project-cols {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }

  /* Narrow screens drop the hero collage entirely and lead with the title.
     The images are loading="lazy", so display:none also stops them being
     fetched at all -- hiding them must not cost mobile bandwidth. */
  .project-titleblock-row {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }

  .project-hero-figure {
    display: none;
  }

  .project-team {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .project-section {
    padding: 1.75rem 0;
  }
}

/* Compact topbar: applied when the single-line nav would overflow (JS), so
   links never wrap -- they fold into a hamburger instead. XL left, circular
   controls right, section links in a panel under the bar. */
.project-topbar.is-compact .project-topbar-inner {
  flex-wrap: wrap;
  gap: 0.5rem 0.65rem;
  padding: 0.65rem var(--page-pad);
}

.project-topbar.is-compact .project-brand {
  font-size: 1.5rem;
  letter-spacing: -0.05em;
  white-space: nowrap;
}

.project-topbar.is-compact .project-brand-full {
  display: none;
}

.project-topbar.is-compact .project-brand-short {
  display: inline;
}

.project-topbar.is-compact .project-topbar-controls {
  order: 2;
  margin-left: auto;
  /* Match homepage `.nav-controls` gap (`--space-sm`). */
  gap: 0.5rem;
}

.project-topbar.is-compact .project-nav-toggle {
  display: flex;
}

/* Match homepage theme/lang toggles (40px), not the 44px hamburger target. */
.project-topbar.is-compact .project-present,
.project-topbar.is-compact .project-home {
  width: 40px;
  height: 40px;
  padding: 0;
  justify-content: center;
  border-radius: 50%;
}

.project-topbar.is-compact .project-present span,
.project-topbar.is-compact .project-home span {
  display: none;
}

.project-topbar.is-compact .project-present svg,
.project-topbar.is-compact .project-home svg {
  width: 18px;
  height: 18px;
}

.project-topbar.is-compact .project-topnav {
  order: 3;
  width: 100%;
  flex-basis: 100%;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: 0.2rem;
  margin-left: 0;
  /* Match homepage `.nav-links` mobile collapse (styles.css ~3381). */
  min-height: 0;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 400ms ease, opacity 250ms ease, padding 250ms ease;
}

.project-topbar.is-compact .project-topnav.is-open {
  max-height: min(70vh, 22rem);
  opacity: 1;
  padding-top: 0.5rem;
  padding-bottom: 1rem;
}

.project-topbar.is-compact .project-topnav a {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 48px;
  padding: 0.625rem 0.75rem 0.625rem 0;
  box-sizing: border-box;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.35;
  white-space: normal;
  flex-shrink: 1;
  -webkit-tap-highlight-color: transparent;
}

.project-topbar.is-compact .project-topnav a::after {
  bottom: 0.5rem;
}

.project-topbar.is-compact .project-topnav a.active::after {
  width: calc(100% - 0.75rem);
}

.project-topbar.is-compact .project-topnav a:active {
  background-color: var(--color-bg-alt);
}

body.project-nav-open {
  overflow: hidden;
}

/* ============================================
   PRESENTATION MODE
   Each hero/section becomes a full-screen slide; arrows flip, Esc exits.
   ============================================ */
.project-present {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
  padding: 0.4rem 0.8rem;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 550;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease,
    transform 0.15s ease;
}

@media (hover: hover) and (pointer: fine) {
  .project-present:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
    background: var(--color-bg-alt);
    transform: translateY(-1px);
  }
}

.project-present:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

body.present-mode {
  overflow: hidden;
}

body.present-mode .project-topbar,
body.present-mode .project-footer,
body.present-mode .back-to-top,
body.present-mode .skip-link {
  display: none;
}

body.present-mode .project-titleblock,
body.present-mode .project-section {
  display: none;
}

body.present-mode .slide-active {
  display: flex;
  flex-direction: column;
  /* `safe` keeps the top reachable when a slide is taller than the screen */
  justify-content: safe center;
  gap: clamp(1.5rem, 3.5vh, 2.25rem);
  position: fixed;
  inset: 0;
  z-index: 60;
  margin: 0;
  padding: 7vh 9vw;
  overflow-y: auto;
  background: var(--color-bg);
  border: 0;
  max-width: none;
  /* Slides read from a distance: scale the whole type ramp up */
  font-size: 1.22em;
}

/* Every slide child shares one left rail and the full content width, so a
   slide never reads as two competing columns. */
body.present-mode .slide-active > * {
  width: 100%;
  max-width: none;
  margin-left: 0;
  margin-right: auto;
}

/* Text is capped for line length while media keeps the full width. These
   selectors carry the section class so they outrank the page's own story rule
   (.project-page--story .project-section > .lead, max-width: none), which
   otherwise let headings and leads run full width while body copy sat narrow
   and centred. */
body.present-mode .project-section.slide-active > h2,
body.present-mode .project-section.slide-active > p,
body.present-mode .project-section.slide-active > ul,
body.present-mode .project-section.slide-active > .project-metrics {
  max-width: 54rem;
}

/* The title slide's own two-column grid needs the wider measure too. */
body.present-mode .project-titleblock.slide-active > * {
  max-width: 74rem;
}

/* The title slide is for the title: drop the download/BibTeX buttons. The
   venue banner stays -- it fills the text column and reads like the venue
   line on an academic title slide. */
body.present-mode .project-titleblock .project-links {
  display: none;
}

/* rem-based titleblock type ignores the deck's em scaling, so size the
   title slide explicitly: this is the one slide that must feel like a
   full-bleed title card. */
body.present-mode .project-titleblock.slide-active h1 {
  font-size: clamp(3.2rem, 6.5vw, 5.4rem);
  line-height: 1.02;
}

body.present-mode .project-titleblock.slide-active .project-subtitle {
  font-size: clamp(1.3rem, 2.2vw, 1.7rem);
  line-height: 1.4;
}

body.present-mode .project-titleblock.slide-active .project-authors {
  font-size: 1.15rem;
}

body.present-mode .project-titleblock.slide-active .project-badge {
  font-size: 0.85rem;
}

/* Slide title: bigger and tighter than the in-page heading so it reads as a
   deck title, not a scroll-page subhead. */
body.present-mode .slide-active h2 {
  font-size: clamp(2rem, 4.2vw, 3.1rem);
  line-height: 1.1;
}

/* `.lead` is each page's concise framing sentence -- always the point of the
   slide, always shown, and given the deck's most prominent body type. */
body.present-mode .slide-active p.lead {
  font-size: 1.35em;
  line-height: 1.5;
  color: var(--color-text);
}

/* A second, denser prose paragraph is exactly the "read the paper for this
   part" detail -- but only once the slide already has a visual or a stat
   row to carry it (.project-cols / .project-metrics as a sibling). Where a
   section is text-only (no figure, no stat tiles -- e.g. a methodology
   summary), that second paragraph IS the slide's content, so it stays;
   cutting it there would leave a near-empty slide, which is the opposite of
   "content-rich, full layout." A paragraph immediately followed by a <ul>
   also stays, since it is that list's lead-in, not a redundant recap. */
body.present-mode
  .project-section.slide-active:has(> .project-cols, > .project-metrics)
  > p:not(.lead):not(:has(+ ul)) {
  display: none;
}

/* Bullet lists are native slide material (scannable, already terse) and
   always stay -- this also covers the team photo grid, which is itself a
   <ul>. */
body.present-mode .slide-active ul {
  margin: 0;
  padding-left: 1.3em;
}

body.present-mode .slide-active ul li {
  margin: 0 0 0.5em;
  line-height: 1.5;
}

/* Stat tiles are the other native slide material: let the numbers carry the
   slide the way they would on a results slide in a talk. */
body.present-mode .slide-active .project-metrics {
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1rem;
}

body.present-mode .slide-active .project-metric {
  padding: 1.15rem 1.3rem;
}

body.present-mode .slide-active .project-metric strong {
  font-size: 2.1rem;
}

body.present-mode .slide-active .project-metric span {
  font-size: 0.95rem;
}

/* Figure captions are the only prose on a purely visual slide, so they stay
   full-length there; a wider clamp than the in-page caption keeps them
   legible from the back of a room without turning into a paragraph. */
body.present-mode .slide-active figcaption {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
  line-clamp: 6;
  overflow: hidden;
  font-size: 0.92em;
  line-height: 1.5;
}

/* Figures keep their aspect ratio but are height-capped, so a two-up row of
   diagrams (plus its captions, plus a trailing list on the same slide, e.g.
   MARIO's Implementation) never forces the slide to scroll. `contain` lets
   the image shrink inside that height without cropping or distorting it. */
body.present-mode .slide-active .project-cols img,
body.present-mode .slide-active .project-cols video {
  max-height: 40vh;
  width: auto;
  max-width: 100%;
  margin-inline: 0;
}

/* Single full-width figure on a slide (e.g. a results chart): same rule as the
   column figures, so its caption tracks the picture width rather than the slide. */
body.present-mode .slide-active > .project-figure {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

body.present-mode .slide-active > .project-figure > img {
  max-height: 52vh;
  width: auto;
  max-width: 100%;
}

body.present-mode .slide-active > .project-figure > figcaption {
  width: 0;
  min-width: 100%;
  font-size: 0.92em;
}

/* The height cap shrinks the image below its column width, so the figure must
   shrink with it: otherwise the caption keeps the full column width and runs
   visibly wider than the picture it describes. `width: 0` keeps the caption
   from contributing to that intrinsic width (its max-content is a single long
   line), and `min-width: 100%` then fills whatever the image resolved to. */
body.present-mode .slide-active .project-cols .project-col {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

body.present-mode .slide-active .project-cols .project-col > figcaption {
  width: 0;
  min-width: 100%;
}

/* Three-up must follow the two-up fit-content rules above: equal specificity
   would otherwise let those undo the equal-track sizing. */
body.present-mode .slide-active .project-cols--three .project-col {
  width: 100%;
  max-width: 100%;
  margin-inline: 0;
}

body.present-mode .slide-active .project-cols--three .project-col > img,
body.present-mode .slide-active .project-cols--three .project-col > video {
  /* Cap width from the height budget so all three stay 4:3 and the same size
     even when max-height alone would leave width:100% taller than allowed. */
  width: min(100%, calc(40vh * 4 / 3));
  max-width: 100%;
  height: auto;
  max-height: 40vh;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  margin-inline: auto;
}

body.present-mode .slide-active .project-cols--three .project-col > figcaption {
  width: auto;
  min-width: 0;
}

/* A trailing list is dense (title + lead + two figures + captions + list),
   but the diagrams are the slide's point, so keep a real height budget rather
   than shrinking them into the leftover strip under the lead. */
body.present-mode
  .project-section.slide-active:has(> .project-cols ~ ul)
  .project-cols img,
body.present-mode
  .project-section.slide-active:has(> .project-cols ~ ul)
  .project-cols video {
  max-height: 30vh;
}

body.present-mode .project-section.slide-active:has(> .project-cols ~ ul) {
  gap: clamp(0.85rem, 2vh, 1.25rem);
}

body.present-mode
  .project-section.slide-active:has(> .project-cols ~ ul)
  figcaption {
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

body.present-mode
  .project-section.slide-active:has(> .project-cols ~ ul)
  ul li {
  margin-bottom: 0.3em;
}

/* Safety net for short viewports (a laptop screen used unprojected, a small
   external display): scale the whole deck down a notch so even the densest
   slide fits without scrolling, rather than tuning every section by hand
   for every possible screen height. */
@media (max-height: 760px) {
  body.present-mode .slide-active {
    padding: 4.5vh 8vw;
    gap: clamp(1rem, 2.5vh, 1.5rem);
    font-size: 1.05em;
  }

  body.present-mode .slide-active h2 {
    font-size: clamp(1.6rem, 3.6vw, 2.4rem);
  }

  body.present-mode .slide-active p.lead {
    font-size: 1.2em;
  }

  body.present-mode .slide-active .project-cols img,
  body.present-mode .slide-active .project-cols video {
    max-height: 26vh;
  }

  body.present-mode .slide-active .project-cols--three .project-col > img,
  body.present-mode .slide-active .project-cols--three .project-col > video {
    width: min(100%, calc(26vh * 4 / 3));
    max-height: 26vh;
  }

  body.present-mode
    .project-section.slide-active:has(> .project-cols ~ ul)
    .project-cols img,
  body.present-mode
    .project-section.slide-active:has(> .project-cols ~ ul)
    .project-cols video {
    max-height: 22vh;
  }

  body.present-mode .slide-active .present-only img {
    max-height: 30vh;
  }

  body.present-mode .slide-active .present-duo img {
    max-height: 24vh;
  }

  body.present-mode .slide-active .project-team {
    gap: 1.1rem;
  }

  body.present-mode .slide-active .project-team img {
    width: 5.5rem;
    height: 5.5rem;
  }

  body.present-mode .slide-active .present-closing {
    margin-top: 0.75rem;
    padding-top: 1rem;
    font-size: 0.95em;
  }

  body.present-mode .project-titleblock.slide-active h1 {
    font-size: clamp(2.4rem, 5vw, 3.4rem);
  }

  body.present-mode .project-titleblock.slide-active .project-subtitle {
    font-size: clamp(1.1rem, 1.9vw, 1.35rem);
  }

  body.present-mode .project-titleblock.slide-active .project-venue img {
    max-height: 13vh;
    width: auto;
  }
}

/* Team photos: full slide, so let the grid breathe and the faces read from
   a distance. */
body.present-mode .slide-active .project-team {
  grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: 1.75rem;
  max-width: 64rem;
  margin-left: 0;
}

body.present-mode .slide-active .project-team img {
  width: 7rem;
  height: 7rem;
}

body.present-mode .slide-active .project-team a {
  gap: 0.8rem;
  font-size: 1.05em;
}

/* Numbered contributions: the "what we claim" slide element. Sized between
   the lead sentence and body text so it reads as the slide's substance. */
body.present-mode .slide-active .present-contrib {
  margin: 0;
  padding-left: 1.4em;
  max-width: 54rem;
  font-size: 1.02em;
}

body.present-mode .slide-active .present-contrib li {
  margin-bottom: 0.55em;
  line-height: 1.5;
  color: var(--color-text-muted);
}

body.present-mode .slide-active .present-contrib li::marker {
  color: var(--color-accent);
  font-weight: 650;
}

body.present-mode .slide-active .present-contrib strong {
  color: var(--color-text);
}

/* Closing line under the team: full paper title, venue, and the site, so the
   deck ends like a talk's thank-you slide instead of trailing off. */
body.present-mode .slide-active .present-closing {
  margin-top: 1rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--color-border-light);
  color: var(--color-text-muted);
  font-size: 1.05em;
  line-height: 1.6;
}

body.present-mode .slide-active .present-closing strong {
  display: block;
  font-family: var(--font-serif);
  font-size: 1.15em;
  color: var(--color-text);
  margin-bottom: 0.35rem;
}

/* Deck-only content: hidden while reading the page, shown on its slide.
   The inverse, .present-hide, drops page prose that would repeat what a
   later slide (or the presenter) already covers. */
.present-only {
  display: none;
}

body.present-mode .slide-active .present-only {
  display: block;
}

body.present-mode .slide-active .present-hide {
  display: none;
}

body.present-mode .slide-active figure.present-only {
  margin: 0;
}

body.present-mode .slide-active .present-only img {
  max-height: 38vh;
  width: auto;
  max-width: 100%;
  margin: 0;
  border: 1px solid var(--color-border-light);
  border-radius: 4px;
}

/* Overview slide: the framing sentence runs full width, then the bullets sit
   beside the visual. Grid placement does this without a wrapper element, so
   the reading page keeps its plain top-to-bottom flow. */
body.present-mode .project-section.slide-active:has(> .present-contrib) {
  display: grid;
  grid-template-columns: 1.02fr 0.98fr;
  column-gap: clamp(2rem, 4vw, 3.5rem);
  row-gap: clamp(1rem, 2.5vh, 1.75rem);
  align-content: center;
}

body.present-mode .slide-active:has(> .present-contrib) > p {
  grid-column: 1 / -1;
}

/* Bullets stretch to the row height and spread, so the text column ends level
   with the image instead of the image being shrunk down to the text. */
/* Bullets fill the row height so the column ends level with the image, but
   they stay a single centred group: space-between would tear the three items
   apart with big gaps and break the list into three floating blocks. */
body.present-mode .slide-active:has(> .present-contrib) > .present-contrib {
  grid-column: 1;
  align-self: stretch;
  max-width: none;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(0.6rem, 1.8vh, 1.35rem);
}

body.present-mode .slide-active:has(> .present-contrib) > .present-contrib > li {
  margin-bottom: 0;
}

body.present-mode
  .slide-active:has(> .present-contrib)
  > .present-only:not(.present-contrib) {
  grid-column: 2;
  align-self: center;
  max-width: none;
  min-width: 0;
}

/* :not(.present-duo) keeps this off the stacked pair below, whose own rule is
   less specific and would otherwise lose to it. */
body.present-mode
  .project-section.slide-active:has(> .present-contrib)
  .present-only:not(.present-contrib):not(.present-duo) img {
  max-height: 46vh;
  width: 100%;
  height: auto;
  object-fit: contain;
}

/* Two deck images stack inside the media column instead of splitting it. */
body.present-mode .slide-active:has(> .present-contrib) .present-duo {
  flex-direction: column;
  gap: 0.8rem;
}

body.present-mode .slide-active:has(> .present-contrib) .present-duo img {
  max-height: 22vh;
  width: 100%;
}

/* A deck-only pair sits side by side, height-matched, each keeping its own
   aspect ratio -- layout adapts to the images, never the other way round. */
body.present-mode .slide-active .present-duo {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  gap: 1.25rem;
}

body.present-mode .slide-active .present-duo img {
  max-height: 32vh;
  margin: 0;
  min-width: 0;
  flex: 0 1 auto;
}

body.present-mode .present-controls,
body.present-mode .present-counter {
  display: flex;
}

.present-controls {
  display: none;
  position: fixed;
  left: 1.25rem;
  bottom: 1.1rem;
  z-index: 70;
  gap: 0.5rem;
}

.present-controls button {
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.present-controls button:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

.present-controls button:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.present-controls button:disabled {
  opacity: 0.35;
  cursor: default;
}

.present-controls button:disabled:hover {
  border-color: var(--color-border);
  color: var(--color-text);
}

.present-counter {
  display: none;
  position: fixed;
  right: 1.25rem;
  bottom: 1.35rem;
  z-index: 70;
  font-size: 0.85rem;
  color: var(--color-text-light);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: no-preference) {
  body.present-mode .slide-active {
    animation: projectFadeInUp 0.35s ease-out both;
  }
}
