/* ===== CSS Reset & Custom Properties ===== */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --color-bg: #ffffff;
  --color-bg-alt: #fbfff7;
  --color-text: #1d1d1f;
  --color-text-secondary: #86868b;
  --color-accent: #21a010;
  --color-accent-hover: #329415;
  --color-highlight-bg: rgba(255, 214, 10, 0.15);
  --color-border: #d2d2d7;
  --color-border-light: #f0f0f0;
  --color-card-bg: #ffffff;
  /* The two latent spaces, wherever a figure draws both: the frozen G1 source is
     pink, our distilled latent is green. Validated as a categorical pair on the
     card surface -- all-pairs CVD dE 13.5 (protan), normal-vision 33.2, both
     above 3:1. The luminance gap is what carries the pair through red-green CVD,
     so keep the green dark in light mode and the pink light in dark mode. */
  --acc-src: #fa2f2f;
  --acc-tgt: #3cd30e;
  --font-primary: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Lato', 'Helvetica Neue', sans-serif;
  --max-width: 1280px;
  --section-padding: clamp(80px, 10vh, 120px) clamp(20px, 5vw, 40px);
  --transition-reveal: 0.8s cubic-bezier(0.16, 1, 0.3, 1);
  --transition-fast: 0.3s ease;
}

/* ===== Dark Mode ===== */
body.dark-mode {
  --color-bg: #000000;
  --color-bg-alt: #1c1c1e;
  --color-text: #f5f5f7;
  --color-text-secondary: #a1a1a6;
  --color-accent: #64fa3f;
  --color-accent-hover: #71f34a;
  --color-highlight-bg: rgba(255, 214, 10, 0.12);
  --color-border: #38383a;
  --color-border-light: #2c2c2e;
  --color-card-bg: #252525;
  /* Re-stepped for the dark card, not flipped: all-pairs CVD dE 10.2 (deutan),
     normal-vision 36.2, both above 3:1 on #252525. */
  --acc-src: #e05c9c;
  --acc-tgt: #0fb345;
}

body.dark-mode .nav-top {
  background: rgba(0, 0, 0, 0.72);
  border-bottom-color: rgba(255, 255, 255, 0.08);
}

body.dark-mode .pub-links a {
  background: var(--color-bg-alt);
  color: var(--color-text);
  border-color: var(--color-border);
}

body.dark-mode .pub-links a:hover {
  background: var(--color-accent);
  color: #000;
  border-color: var(--color-accent);
}

body.dark-mode .bibtex-block {
  background: var(--color-bg-alt);
}

/* ===== Font Faces (Lato) ===== */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 400;
  src: local('Lato Regular'), local('Lato-Regular'), url(https://fonts.gstatic.com/s/lato/v15/S6uyw4BMUTPHjx4wXiWtFCc.woff2) format('woff2');
}
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 700;
  src: local('Lato Bold'), local('Lato-Bold'), url(https://fonts.gstatic.com/s/lato/v15/S6u9w4BMUTPHh6UVSwiPGQ3q5d0.woff2) format('woff2');
}

/* ===== Base ===== */
html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  font-family: var(--font-primary);
  font-size: 17px;
  line-height: 1.65;
  color: var(--color-text);
  background: var(--color-bg);
  overflow-x: hidden;
  transition: background 0.3s ease, color 0.3s ease;
}

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

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

h1, h2, h3 {
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

/* ===== Theme Toggle ===== */
.theme-toggle {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text);
  opacity: 0.6;
  padding: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  transition: opacity var(--transition-fast), background var(--transition-fast);
}

.theme-toggle:hover {
  opacity: 1;
  background: rgba(128, 128, 128, 0.1);
}

.icon-sun { display: none; }
.icon-moon { display: block; }
body.dark-mode .icon-sun { display: block; }
body.dark-mode .icon-moon { display: none; }

/* ===== Top Navigation ===== */
.nav-top {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.nav-inner {
  max-width: var(--max-width);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 40px;
  padding: 14px 20px;
}

.nav-link {
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text);
  opacity: 0.7;
  transition: opacity var(--transition-fast);
  text-decoration: none;
}

.nav-home {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-fast);
}

.nav-top.scrolled .nav-home {
  opacity: 0.7;
  pointer-events: auto;
}

.nav-link:hover,
.nav-link.active {
  opacity: 1;
  color: var(--color-text);
}

.nav-top:not(.scrolled) .nav-home {
  opacity: 0;
  pointer-events: none;
}

/* ===== Reveal Animations ===== */
.reveal {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity var(--transition-reveal), transform var(--transition-reveal);
}

.reveal.revealed {
  opacity: 1;
  transform: translateY(0);
}

/* ===== Hero Section ===== */
.hero {
  min-height: 100vh;
  display: flex;
  padding: 48px clamp(20px, 5vw, 40px);   /* horizontal matches .content-section */
  background: var(--color-bg);
  text-align: center;
  position: relative;
}

/* Native, responsive. margin:auto centers it when it fits; on short windows
   the auto margins collapse so content flows from the top and scrolls. */
.hero-inner {
  max-width: var(--max-width);   /* same as .section-inner (abstract) */
  width: 100%;
  margin: auto;
  animation: heroFadeIn 1.2s cubic-bezier(0.16, 1, 0.3, 1) both;
}

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

/* ===== Title — base ===== */
.hero-title {
  font-size: clamp(1.3rem, 3vw, 2.4rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
  padding: 46px 0;
  margin-bottom: 14px;
  color: var(--color-text);
  white-space: nowrap;
  position: relative;
  display: inline-block;
}

/* ===== Hero Authors & Affiliation ===== */
.hero-authors {
  font-size: clamp(15px, 1.55vw, 21px);
  color: var(--color-text-secondary);
  line-height: 1.8;
  margin-bottom: 10px;
}

.hero-authors a {
  color: var(--color-text);
  transition: color var(--transition-fast);
}

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

.hero-authors strong {
  color: var(--color-text);
}

.hero-affiliation {
  font-size: clamp(14px, 1.3vw, 18px);
  color: var(--color-text-secondary);
  margin-bottom: 14px;
}

.hero-affiliation a {
  color: var(--color-text-secondary);
}

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

.hero-venue {
  font-size: clamp(15px, 1.35vw, 19px);
  font-weight: 600;
  letter-spacing: 0.04em;
  margin-top: 4px;
  margin-bottom: 10px;
}

.hero-venue a {
  color: #c5960c;
  transition: color var(--transition-fast);
}

.hero-venue a:hover {
  color: #e0ab1a;
}


/* ===== Publication Link Buttons ===== */
.pub-links {
  display: flex;
  gap: 16px;
  justify-content: center;
  flex-wrap: wrap;
}

.pub-links a {
  font-size: clamp(12px, 1.2vw, 16px);
  font-weight: 500;
  color: var(--color-accent);
  padding: 7px 16px;
  border-radius: 980px;
  border: 1px solid var(--color-accent);
  transition: all var(--transition-fast);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.pub-links a:hover {
  background: var(--color-accent);
  color: #fff;
}

.pub-links a svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

/* ===== Content Sections ===== */
.content-section {
  padding: var(--section-padding);
  background: var(--color-bg);
  position: relative;
}

.content-section:nth-child(even) {
  background: var(--color-bg-alt);
}

.section-inner {
  max-width: var(--max-width);
  width: 100%;
  margin: 0 auto;
}

.section-heading {
  font-size: clamp(2rem, 4vw, 3.5rem);
  color: var(--color-text);
  margin-bottom: 36px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--color-border);
}

.section-body {
  font-size: 16px;
  line-height: 1.75;
  color: var(--color-text);
}

.section-body p {
  margin-bottom: 16px;
}

.placeholder-block {
  background: var(--color-card-bg);
  border-radius: 16px;
  padding: 60px 40px;
  text-align: center;
  color: var(--color-text-secondary);
  font-size: 15px;
  margin-top: 24px;
  border: 1px dashed var(--color-border);
}

/* ===== BibTeX ===== */
.bibtex-block {
  background: var(--color-bg-alt);
  border-radius: 12px;
  padding: 24px 28px;
  overflow-x: auto;
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  font-size: 14px;
  line-height: 1.6;
  color: var(--color-text);
  margin-top: 16px;
}

/* ===== Interactive Demo Panel ===== */
.demo-panel {
  margin-bottom: 6px;
}

.demo-title {
  font-size: clamp(1rem, 1.6vw, 1.25rem);
  font-weight: 600;
  color: var(--color-text);
  text-align: center;
  margin: 0 0 16px;
  letter-spacing: 0.01em;
}

.demo-images {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 16px;
  align-items: start;
}

.demo-col {
  text-align: center;
}

.demo-img-wrapper {
  position: relative;
  cursor: crosshair;
  overflow: hidden;
  border-radius: 8px;
  background: var(--color-card-bg);
  line-height: 0;
}

.demo-img-wrapper img {
  width: 100%;
  height: auto;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

.demo-roi {
  position: absolute;
  border: 2px solid #ff3b30;
  border-radius: 3px;
  pointer-events: none;
  z-index: 2;
  /* Size set by JS: 25% x 25% of the image */
  width: 25%;
  height: 25%;
  top: 0;
  left: 0;
}

.demo-roi-hint {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 10px;
  font-weight: 600;
  text-align: center;
  line-height: 1.3;
  opacity: 0.9;
  transition: opacity 0.3s ease;
}

.demo-img-wrapper:hover .demo-roi-hint {
  opacity: 0;
  pointer-events: none;
}

.demo-col canvas {
  width: 100%;
  border-radius: 8px;
  background: var(--color-card-bg);
  display: block;
}

#demo-naive-canvas {
  border: 2px dashed #ff3b30;
}

#demo-foveated-canvas {
  border: 2px solid #ff3b30;
}

.demo-label {
  font-size: 13px;
  color: var(--color-text-secondary);
  margin-top: 8px;
  font-weight: 500;
}

.demo-caption {
  font-size: 13px;
  color: var(--color-text-secondary);
  line-height: 1.65;
  margin-top: 16px;
  text-align: justify;
}

/* ===== Hero Tagline (typewriter) ===== */
.hero-tagline-wrapper {
  text-align: center;
}

.tagline-section {
  padding: 56px 20px;
  background: var(--color-bg);
  text-align: center;
}

.hero-tagline-line {
  font-size: clamp(12px, 1.1vw, 15px);
  font-weight: 400;
  color: var(--color-text);
  line-height: 1.8;
  letter-spacing: 0.01em;
  min-height: 1.8em;
  margin: 0;
  display: block;
}

/* ===== Scroll Down Arrow ===== */
.scroll-arrow {
  display: block;
  position: absolute;
  bottom: 30px;
  left: 50%;
  transform: translateX(-50%);
  color: var(--color-text-secondary);
  opacity: 0.5;
  text-decoration: none;
  cursor: pointer;
  animation: bounceArrow 2s ease-in-out infinite;
  z-index: 10;
  padding: 10px;
}

/* Hero scroll arrow: in-flow, right below buttons */
.hero-scroll-arrow {
  position: static;
  transform: none;
  margin-top: 8px;
  display: inline-block;
  animation: bounceArrowInline 2s ease-in-out infinite;
}

@keyframes bounceArrowInline {
  0%, 100% { transform: translateY(0); opacity: 0.5; }
  50% { transform: translateY(10px); opacity: 1; }
}

.scroll-arrow:hover {
  opacity: 1;
  color: var(--color-accent);
}

.hero-scroll-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}

.hero-scroll-cta {
  display: flex;
  align-items: center;
  gap: 10px;
}

.scroll-hint {
  font-size: 12px;
  color: var(--color-text-secondary);
  opacity: 0.6;
  letter-spacing: 0.05em;
}

@keyframes bounceArrow {
  0%, 100% { transform: translateX(-50%) translateY(0); opacity: 0.5; }
  50% { transform: translateX(-50%) translateY(10px); opacity: 1; }
}

/* ===== Footer ===== */
.site-footer {
  text-align: center;
  padding: 40px 20px;
  font-size: 13px;
  color: var(--color-text-secondary);
  border-top: 1px solid var(--color-border);
}

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

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

/* ===== Bandwidth Comparison Demo ===== */
.bandwidth-demo {
  margin: 32px 0;
}

.bandwidth-tabs {
  margin-bottom: 16px;
}

.bw-overlay-label {
  position: absolute;
  top: 8px;
  left: 8px;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 4px;
  z-index: 3;
}

.bw-slider-row {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 10px;
  margin-top: 8px;
}

.bw-slider-spacer {
  /* Empty spacer matching the left panel width */
}

.bw-slider-wrap {
  text-align: center;
}

.bw-slider-label {
  font-size: 11px;
  color: var(--color-text-secondary);
  display: block;
  margin-top: 4px;
}

.bw-slider {
  width: 100%;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  height: 4px;
  background: var(--color-border);
  border-radius: 2px;
  outline: none;
}

.bw-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--color-accent);
  cursor: pointer;
}

.bw-slider::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--color-accent);
  cursor: pointer;
  border: none;
}

.bw-slider-ticks {
  display: flex;
  justify-content: space-between;
  font-size: 10px;
  color: var(--color-text-secondary);
  margin-top: 2px;
  padding: 0 2px;
}

/* ===== Video Carousel & 3-Panel Grid ===== */
.video-3panel {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
  align-items: start;
}

.panel {
  text-align: center;
}

.panel-loading-wrap {
  position: relative;
  aspect-ratio: 1020 / 574;
  background: var(--color-card-bg);
  border-radius: 8px;
  overflow: hidden;
}

.panel-loader {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-card-bg);
  z-index: 2;
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text-secondary);
}

.frame-loader {
  border-radius: 8px;
}

.video-category-label {
  position: absolute;
  top: 8px;
  left: 8px;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 4px;
  z-index: 3;
  letter-spacing: 0.02em;
}

.video-caption {
  font-size: 14px;
  color: var(--color-text-secondary);
  line-height: 1.6;
  text-align: justify;
  margin-top: 6px;
  min-height: 2.4em;
}

.panel video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 8px;
  display: block;
  background: var(--color-card-bg);
}

.panel-roi video {
  border: 2px solid #ff3b30;
}

.frame-slideshow {
  position: relative;
  width: 100%;
  aspect-ratio: 2040 / 1148;
  border-radius: 8px;
  overflow: hidden;
  background: var(--color-card-bg);
}

.slideshow-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity 0.2s ease;
}

/* Frame slideshow controls */
.frame-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 2px;
}

.frame-arrow {
  width: 24px;
  height: 24px;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: none;
  cursor: pointer;
  opacity: 0.5;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity var(--transition-fast);
}

.frame-arrow:hover {
  opacity: 1;
}

.frame-dots {
  display: flex;
  gap: 6px;
}

.frame-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-border);
  border: none;
  cursor: pointer;
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.frame-dot.active {
  background: var(--color-accent);
  transform: scale(1.3);
}

.panel-frames {
  aspect-ratio: 2040 / 1148;
}

/* ===== Carousel Controls ===== */
.carousel-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 4px;
}

.carousel-arrow {
  width: 32px;
  height: 32px;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: none;
  cursor: pointer;
  opacity: 0.6;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity var(--transition-fast), background var(--transition-fast);
}

.carousel-arrow:hover {
  opacity: 1;
  background: rgba(128, 128, 128, 0.1);
}

.carousel-arrow svg {
  width: 18px;
  height: 18px;
}

.carousel-dots {
  display: flex;
  gap: 8px;
}

.carousel-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-border);
  border: none;
  cursor: pointer;
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.carousel-dot.active {
  background: var(--color-accent);
  transform: scale(1.2);
}

/* ===== Video Carousel Wrapper ===== */
.video-carousel {
  margin-top: 24px;
}

.hero-carousel {
  margin-top: 16px;
  margin-bottom: 4px;
}

/* ===== Sensor-resolution plot (inside the first teaser panel) ===== */
.isocell-plot-svg {
  width: 100%;
  height: 100%;
  display: block;
}

.plot-axis {
  stroke: var(--color-text-secondary);
  stroke-width: 1;
  opacity: 0.6;
}

.plot-tick line {
  stroke: var(--color-text-secondary);
  stroke-width: 1;
  opacity: 0.6;
}

.plot-tick text {
  font-size: 10px;
  fill: var(--color-text-secondary);
}

.plot-axis-label {
  font-size: 11px;
  fill: var(--color-text-secondary);
  font-weight: 500;
}

.plot-line-solid {
  stroke: #c5960c;
  stroke-dasharray: 600;
  stroke-dashoffset: 600;
  animation: drawSolidLine 1.7s ease-out forwards;
}

@keyframes drawSolidLine {
  to { stroke-dashoffset: 0; }
}

.plot-line-dashed {
  stroke: #c5960c;
  opacity: 0;
  animation: fadeInLine 0.5s ease-out 1.7s forwards;
}

@keyframes fadeInLine {
  to { opacity: 1; }
}

.plot-point {
  opacity: 0;
  animation: fadeInPoint 0.4s ease-out var(--d, 0s) forwards;
}

.plot-point circle {
  fill: #c5960c;
}

.plot-point text {
  font-size: 8px;
  font-weight: 600;
  fill: var(--color-text);
}

.plot-point-prototype circle {
  fill: #c5960c;
  stroke: var(--color-text);
  stroke-width: 0.8;
}

.plot-point-prototype text { font-size: 9px; }

.plot-callout {
  stroke: #c5960c;
  opacity: 0;
  animation: fadeInLine 0.5s ease-out 1.85s forwards;
}

@keyframes fadeInPoint {
  to { opacity: 1; }
}

.hero-teaser-wrap {
  margin-top: 40px;
  margin-bottom: 4px;
}

.hero-teaser {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: nowrap;
  width: 100%;
}

.teaser-panel {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  flex-grow: var(--panel-aspect);
  flex-shrink: 1;
  flex-basis: 0;
}

.teaser-isocell { --panel-aspect: 2.069; }   /* matches plot SVG viewBox 300/145 */
.teaser-global  { --panel-aspect: 1.778; }   /* 16 / 9 — matches foveated crop panel */
.teaser-crop    { --panel-aspect: 1.778; }   /* 16  / 9    */

/* Titles row sits ABOVE the teaser, mirroring the panel flex-grow weights so
   each subtitle lines up over its panel and the plot panel itself stays
   vertically aligned with the global + foveated panels (no title offset). */
/* Equation-style titles: phrases sized to content, operators centered exactly
   between consecutive phrases (equal gap on both sides). Sequential reveal. */
.hero-teaser-titles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-bottom: 14px;
}

.tt-cell {
  flex: 0 0 auto;
  white-space: nowrap;
  text-align: center;
  color: #c5960c;
  font-weight: 600;
  font-size: clamp(15px, 1.35vw, 19px);   /* matches SIGGRAPH 2026 (.hero-venue) */
  line-height: 1.3;
}

.tt-op {
  flex: 0 0 auto;
  color: var(--color-text);
  font-weight: 700;
  font-size: 32px;
}

/* Sequential reveal: phrase1 → + → phrase2 → = → phrase3 */
.hero-teaser-titles > * {
  opacity: 0;
  animation: ttFadeIn 0.5s ease-out forwards;
}
.hero-teaser-titles > *:nth-child(1) { animation-delay: 0.2s; }
.hero-teaser-titles > *:nth-child(2) { animation-delay: 0.75s; }
.hero-teaser-titles > *:nth-child(3) { animation-delay: 1.3s; }
.hero-teaser-titles > *:nth-child(4) { animation-delay: 1.85s; }
.hero-teaser-titles > *:nth-child(5) { animation-delay: 2.4s; }

@keyframes ttFadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.teaser-img-wrap {
  position: relative;
  width: 100%;
  border-radius: 4px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.04);
}

body.dark-mode .teaser-img-wrap {
  background: rgba(255, 255, 255, 0.04);
}

/* Sensor photo has transparent background — let it sit cleanly on any page bg. */
.teaser-isocell .teaser-img-wrap {
  background: transparent;
}

.teaser-isocell .teaser-img-wrap { aspect-ratio: 300 / 145; overflow: visible; }
.teaser-global  .teaser-img-wrap { aspect-ratio: 16 / 9; }
.teaser-crop    .teaser-img-wrap {
  aspect-ratio: 16 / 9;
  box-sizing: border-box;
  border: 2px solid #e51a1a;
  box-shadow: 0 0 12px rgba(229, 26, 26, 0.45);
}

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

.teaser-roi {
  position: absolute;
  border: 2px solid #e51a1a;
  pointer-events: none;
  box-shadow: 0 0 12px rgba(229, 26, 26, 0.45);
  transition: left 0.6s ease, top 0.6s ease, width 0.6s ease, height 0.6s ease;
}

.teaser-label {
  font-size: clamp(12px, 1.1vw, 16px);
  color: var(--color-text);
  text-align: center;
  letter-spacing: 0.02em;
}

.hero-teaser-dots {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 14px;
}

.hero-teaser-credit {
  font-size: 10px;
  color: var(--color-text-secondary);
  opacity: 0.55;
  text-align: center;
  margin-top: 8px;
  letter-spacing: 0.02em;
}

.hero-teaser-blurb {
  font-size: 16px;          /* matches .section-body (abstract text) */
  line-height: 1.75;        /* matches .section-body */
  color: var(--color-text);
  text-align: left;
  margin: 12px 0 0;
}

.teaser-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: none;
  background: rgba(128, 128, 128, 0.4);
  cursor: pointer;
  padding: 0;
  transition: background 0.25s ease, transform 0.25s ease;
}

.teaser-dot:hover {
  background: rgba(128, 128, 128, 0.7);
}

.teaser-dot.active {
  background: #c5960c;
  transform: scale(1.2);
}

/* ===== Panel Labels ===== */
.panel .demo-label {
  font-size: 11px;
  color: var(--color-text-secondary);
  margin-top: 2px;
}

/* ===== Results Section Tabs ===== */
.results-subtitle {
  font-size: clamp(1.2rem, 2.5vw, 1.8rem);
  margin-bottom: 24px;
  color: var(--color-text);
}

.tab-nav {
  display: flex;
  gap: 4px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--color-border);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.tab-btn {
  background: none;
  border: none;
  padding: 12px 20px;
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text-secondary);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color var(--transition-fast), border-bottom-color var(--transition-fast);
  white-space: nowrap;
}

.tab-btn:hover {
  color: var(--color-text);
}

.tab-btn.active {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}

.tab-panel {
  display: none;
}

.tab-panel.active {
  display: block;
}

/* ===== Dark Mode — Carousel & Tabs ===== */
body.dark-mode .carousel-arrow {
  border-color: rgba(255, 255, 255, 0.3);
  color: rgba(255, 255, 255, 0.7);
}

body.dark-mode .carousel-arrow:hover {
  border-color: rgba(255, 255, 255, 0.6);
  color: #fff;
}

body.dark-mode .frame-arrow {
  border-color: rgba(255, 255, 255, 0.3);
  color: rgba(255, 255, 255, 0.7);
}

body.dark-mode .frame-arrow:hover {
  border-color: rgba(255, 255, 255, 0.6);
  color: #fff;
}

body.dark-mode .tab-nav {
  border-bottom-color: var(--color-border);
}

/* ===== Method Video ===== */
.method-video {
  width: 100%;
  border-radius: 12px;
  display: block;
}

.pub-links a.btn-disabled {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
  border-color: var(--color-text-secondary);
  color: var(--color-text-secondary);
}

/* ===== Responsive ===== */
@media (max-width: 768px) {
  .nav-inner {
    gap: 16px;
    padding: 10px 16px;
    justify-content: flex-start;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  .nav-inner::-webkit-scrollbar {
    display: none;
  }

  .nav-link {
    font-size: 12px;
    white-space: nowrap;
    flex-shrink: 0;
  }

  /* Hero stays a full-viewport box; the JS scales the fixed design canvas
     to fit, so no per-element mobile typography overrides are needed. */
  .hero {
    padding: 56px 12px;
  }

  .content-section {
    padding: 60px 16px;
  }

  .section-heading {
    font-size: 1.6rem;
  }

  .placeholder-block {
    padding: 40px 20px;
  }

  .scroll-arrow {
    bottom: 16px;
  }

  .demo-images {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  .demo-label {
    font-size: 11px;
  }

  .video-3panel {
    grid-template-columns: 1fr;
    gap: 12px;
  }

  .tab-btn {
    font-size: 12px;
    padding: 10px 14px;
  }

  .tab-nav {
    gap: 0;
  }
}

/* ===== Speed Template Layer ===== */
.hero-title {
  padding: 60px 0 26px;
  margin-bottom: 2px;
  white-space: normal;
}

.title-p1 {
  display: block;
}

.title-p2-wrap {
  display: block;
  margin-top: 0.12em;
}

.title-p2 {
  display: inline-block;
}

.section-body a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

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

#results-tabs {
  border-bottom: none;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
  overflow-x: visible;
}

#results-tabs .tab-btn {
  border: 1px solid var(--color-text);
  border-radius: 980px;
  padding: 8px 18px;
  color: var(--color-text);
}

#results-tabs .tab-btn:hover {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

#results-tabs .tab-btn.active {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: #fff;
}

.spd-subhead {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--color-text);
  margin: 44px 0 18px;
}

.spd-table-wrap {
  margin-top: 32px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.spd-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  color: var(--color-text);
  white-space: nowrap;
}

.spd-table th,
.spd-table td {
  padding: 7px 14px;
  text-align: center;
}

.spd-table tbody td:first-child,
.spd-table thead tr:first-child th:first-child {
  text-align: left;
}

.spd-table thead th {
  font-weight: 600;
  color: var(--color-text);
}

.spd-table thead tr:first-child th {
  border-top: 2px solid var(--color-text);
  border-bottom: 1px solid var(--color-text);
}

.spd-table thead tr:last-child th {
  border-bottom: 1px solid var(--color-text);
}

.spd-table tbody tr.tier td {
  border-top: 1px solid var(--color-border);
}

.spd-table tbody tr:last-child td {
  border-bottom: 2px solid var(--color-text);
}

.spd-table .ours td:first-child {
  font-weight: 700;
}

/* Narrow screens: the tables are nowrap and wider than a phone, so the wrapper
   scrolls sideways and the robot-name column pins to the left edge — the row
   stays identifiable however far the numbers are scrolled.
   `border-collapse: separate` is required: collapsed borders are painted by the
   table, not the cell, so they would stay behind while the sticky column moves.
   No border doubles up here because only the header and the last row carry rules. */
@media (max-width: 768px) {
  .content-section { --table-bg: var(--color-bg); }
  .content-section:nth-child(even) { --table-bg: var(--color-bg-alt); }
  /* Dark mode flattens every section to one tone -- match it. */
  body.dark-mode .content-section,
  body.dark-mode .content-section:nth-child(even) { --table-bg: #090d12; }

  .spd-table {
    border-collapse: separate;
    border-spacing: 0;
    font-size: 12px;
  }

  .spd-table th,
  .spd-table td {
    padding: 7px 10px;
  }

  .spd-table th:first-child,
  .spd-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--table-bg, var(--color-bg));
    /* 1px seam + a short fade, so the pinned column reads as sitting above the
       numbers sliding under it. */
    box-shadow: 1px 0 0 var(--color-border), 7px 0 7px -7px rgba(0, 0, 0, 0.28);
  }

  body.dark-mode .spd-table th:first-child,
  body.dark-mode .spd-table td:first-child {
    box-shadow: 1px 0 0 var(--color-border), 7px 0 7px -7px rgba(0, 0, 0, 0.75);
  }
}

.tab-panel > details.spd-collapse:first-of-type > summary.spd-subhead {
  margin-top: 4px;
}

.spd-subsubhead {
  font-size: 16px;
  font-weight: 600;
  color: var(--color-text);
  margin: 28px 0 12px;
}

.spd-subsubhead a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
}

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

details.spd-collapse {
  margin: 0;
}

details.spd-collapse > summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
}

details.spd-collapse > summary::-webkit-details-marker {
  display: none;
}

details.spd-collapse > summary::before {
  content: "";
  display: inline-block;
  width: 0;
  height: 0;
  border-left: 7px solid currentColor;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  margin-right: 12px;
  vertical-align: middle;
  transition: transform .2s ease;
}

details.spd-collapse[open] > summary::before {
  transform: rotate(90deg);
}

.speed-template-body {
  margin-bottom: 28px;
}

.pub-links a[aria-disabled="true"] {
  cursor: default;
  pointer-events: none;
}

.template-result-panel .section-body {
  margin-bottom: 12px;
}

body:not(.dark-mode) .hero {
  background: #fdfdfd;
}

body.dark-mode .hero,
body.dark-mode .content-section,
body.dark-mode .content-section:nth-child(even) {
  background: #090d12;
}

@media (max-width: 768px) {
  .hero-title {
    white-space: normal;
    line-height: 1.2;
    padding: 40px 0 22px;
  }
}

/* ===== CrossBFM Layer ===== */

/* Figures. The source PNGs are flattened onto white, so they always sit in a
   light frame regardless of theme; the frame keeps that deliberate. */
.fig {
  margin: 28px 0 0;
}

.fig-frame {
  background: #ffffff;
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 14px;
  overflow: hidden;
  line-height: 0;
}

.fig-frame img {
  width: 100%;
  height: auto;
  display: block;
}

.fig figcaption {
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--color-text-secondary);
  margin-top: 12px;
  text-align: left;
}

.fig figcaption strong {
  color: var(--color-text);
}

/* Hero teaser: the pipeline figure spans the same width as the abstract text. */
.hero-teaser-wrap .fig {
  max-width: none;
  margin: 32px auto 0;
}

.hero-teaser-wrap .fig figcaption {
  text-align: center;
  font-size: 18px;
  line-height: 1.75;
  margin-top: 14px;
}

/* Inline math-ish symbols and identifiers. */
.mono {
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  font-size: 0.92em;
  white-space: nowrap;
}

/* Display equations. */
.eq-block {
  background: var(--color-card-bg);
  border: 1px solid var(--color-border-light);
  border-radius: 12px;
  padding: 18px 22px;
  margin: 20px 0 22px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

body.dark-mode .eq-block {
  border-color: var(--color-border);
}

.eq {
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  font-size: 18px;
  line-height: 2.1;
  color: var(--color-text);
  white-space: nowrap;
  text-align: center;
}

.eq em {
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 1.08em;
}

/* A table figure sits between two runs of prose rather than above a divider, so
   it needs the trailing gap the image figures get from what follows them. */
.fig-table {
  margin-bottom: 34px;
}

/* Table captions, matching figure captions. */
.table-caption {
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--color-text-secondary);
  margin: 12px 0 0;
  white-space: normal;
}

/* Reached by screen readers only — a <caption> naming a table whose visible
   caption sits outside it, so the table is not announced unlabelled. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* TL;DR callout at the top of the abstract. */
.tldr {
  border-left: 3px solid var(--color-accent);
  padding: 2px 0 2px 16px;
}

@media (max-width: 1200px) {
  .fig-frame {
    padding: 8px;
    border-radius: 8px;
  }

  .eq {
    font-size: 12px;
  }

  .eq-block {
    padding: 14px;
  }
}

/* Hero fits one viewport: the teaser figure replaces some of the title padding. */
.hero .hero-title {
  padding: 30px 0 16px;
}

.hero-teaser-wrap {
  margin-top: 20px;
}

.hero-teaser-wrap .fig {
  max-width: none;
  margin-top: 0;
}

/* Portrait-ish plots read better constrained; wide panels stay full width. */
.fig-narrow {
  max-width: 820px;
  margin-left: auto;
  margin-right: auto;
}

/* ===== CrossBFM Wordmark =====
   The project name carries the hero: it is set large and gradient-filled, and
   the subtitle drops in size so the two read as name + descriptor rather than
   one long title. The blue -> green ramp mirrors the pipeline figure's
   source -> target palette. */
.hero-title .title-p1 {
  /* inline-block so the gradient box hugs the word rather than the hero width,
     which would clip both ends of the ramp. */
  display: inline-block;
  font-size: clamp(2.6rem, 7.6vw, 5.3rem);
  font-weight: 1500;
  letter-spacing: -0.045em;
  line-height: 1;
  margin-bottom: 0.2em;
  color: var(--color-text);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero-title .title-p1 {
    background-image: linear-gradient(100deg, #79e659 0%, #31a740 45%, #1d6b0a 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }

  body.dark-mode .hero-title .title-p1 {
    background-image: linear-gradient(100deg, #79e659 0%, #31a740 45%, #27aa06 100%);
  }
}

.hero-title .title-p2 {
  font-size: clamp(1.05rem, 2.15vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--color-text);
}

@media (max-width: 768px) {
  .hero-title .title-p1 {
    font-size: clamp(2.1rem, 9vw, 2.8rem);
  }
}

/* ===== Results Subsections =====
   Three stacked subsections, all visible at once — no tabs and no collapsing. */
.results-block {
  margin-top: 56px;
  padding-top: 40px;
  border-top: 1px solid var(--color-border);
}

.results-subhead {
  display: flex;
  align-items: baseline;
  gap: 14px;
  font-size: clamp(1.35rem, 2.4vw, 1.9rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--color-text);
  margin-bottom: 20px;
}

.results-kicker {
  font-size: 0.62em;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

.results-subsubhead {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--color-text);
  margin: 30px 0 10px;
}

.results-block .fig {
  margin-top: 24px;
}

.results-block .spd-table-wrap {
  margin-top: 26px;
}

/* ===== Latin Modern (equations) =====
   Self-hosted subsets of the GUST Latin Modern faces, so the display equations
   match the paper regardless of what the visitor has installed. LM Math is
   listed second in the stack purely as a per-glyph fallback: the text faces
   carry no sum sign or Greek. */
@font-face {
  font-family: 'LM Roman';
  src: url('assets/fonts/lmroman10-regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'LM Roman';
  src: url('assets/fonts/lmroman10-italic.woff2') format('woff2');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'LM Roman';
  src: url('assets/fonts/lmroman10-bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'LM Math';
  src: url('assets/fonts/latinmodern-math.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

.eq {
  font-family: 'LM Roman', 'LM Math', 'Latin Modern Roman', 'Latin Modern Math',
    'CMU Serif', 'Computer Modern', Georgia, serif;
  font-size: 20px;
  line-height: 2;
  letter-spacing: 0;
}

/* Variables are italic in the same face, not a different serif. */
.eq em {
  font-family: inherit;
  font-size: 1em;
  font-style: italic;
}

@media (max-width: 768px) {
  .eq {
    font-size: 16px;
  }
}

/* Hat accent drawn as a positioned circumflex: Latin Modern has no precomposed
   z-hat, and a combining mark over an unanchored base (D) collapses into the
   letter. */
.eq .hat,
.mm-eq .hat {
  position: relative;
  display: inline-block;
  line-height: 1;
}

.eq .hat::after,
.mm-eq .hat::after {
  content: "^";
  position: absolute;
  left: 0;
  right: 0;
  top: 0.12em;
  text-align: center;
  line-height: 1;
  pointer-events: none;
}

.eq .hat.cap::after {
  top: -0.22em;
}

/* ===== Bar chart (data scaling) =====
   Single-hue ordinal green ramp, light->dark with the corpus fraction. Both the
   light and dark steps pass the ordinal palette checks (monotone lightness,
   >=0.06 adjacent gaps, light end clearing its surface). */
.chart-card {
  background: var(--color-card-bg);
  border: 1px solid var(--color-border-light);
  border-radius: 12px;
  padding: 20px 16px 12px;
}

body.dark-mode .chart-card {
  border-color: var(--color-border);
}

.barchart {
  display: block;
  width: 100%;
  height: auto;
  font-family: var(--font-primary);
}

.barchart .grid line {
  stroke: var(--color-border);
  stroke-width: 1;
}

.barchart .grid line.axis {
  stroke: var(--color-text-secondary);
}

.barchart .control-rule {
  stroke: var(--color-border);
  stroke-width: 1;
}

.barchart .ytick text {
  font-size: 13px;
  fill: var(--color-text-secondary);
}

.barchart .axis-title {
  font-size: 14px;
  font-weight: 500;
  fill: var(--color-text-secondary);
}

.barchart .axis-note {
  font-size: 12px;
  fill: var(--color-text-secondary);
  opacity: 0.75;
}

.barchart .val {
  font-size: 15px;
  font-weight: 600;
  fill: var(--color-text);
}

.barchart .cat {
  font-size: 15px;
  fill: var(--color-text-secondary);
}

.barchart .bar path {
  transition: opacity var(--transition-fast);
}

.barchart .bar:hover path {
  opacity: 0.78;
}

.barchart .b1 path { fill: #8bbd50; }
.barchart .b2 path { fill: #76a738; }
.barchart .b3 path { fill: #61911c; }
.barchart .b4 path { fill: #4e7b00; }
.barchart .b5 path { fill: #3a6700; }
.barchart .b6 path { fill: #285200; }
.barchart .b7 path { fill: #153f00; }

body.dark-mode .barchart .b1 path { fill: #c3f98a; }
body.dark-mode .barchart .b2 path { fill: #ade173; }
body.dark-mode .barchart .b3 path { fill: #97ca5d; }
body.dark-mode .barchart .b4 path { fill: #82b346; }
body.dark-mode .barchart .b5 path { fill: #6d9d2d; }
body.dark-mode .barchart .b6 path { fill: #59880a; }
body.dark-mode .barchart .b7 path { fill: #457200; }

/* ===== Bar chart (tracking & goal reaching) =====
   The two arms differ only in conditioning, so they are a categorical pair and
   not a ramp: the green end of the ordinal scale for the latent arm -- ours, and
   the only arm goal reaching has -- against a slate blue for the reference-fed
   baseline. Each pair was validated against the surface it actually sits on,
   #ffffff light and #252525 dark; the goal bars are green because they are the
   same latent arm asked a different question. */
.barchart .mae-latent path { fill: #8bbd50; }
.barchart .mae-joint path { fill: #77acee; }

body.dark-mode .barchart .mae-latent path { fill: #c3f98a; }
body.dark-mode .barchart .mae-joint path { fill: #77acee; }

/* +/- 1 std over 3 seeds. The whisker crosses from inside the bar to the card
   above it, so it takes the ink token, which reads on both. */
.barchart .bar path.err {
  fill: none;
  stroke: var(--color-text);
  stroke-width: 1.5;
  opacity: 0.5;
}

/* Goal reaching is a different task, not a third arm -- a reference-fed policy
   has no latent to hold at all -- so the rule marks a break in kind. */
.barchart .split-rule {
  stroke: var(--color-text-secondary);
  stroke-width: 1.5;
  stroke-dasharray: 6 5;
  opacity: 0.65;
}

.barchart .band {
  font-size: 14px;
  font-weight: 600;
  fill: var(--color-text-secondary);
}

.mae-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 20px;
  margin-bottom: 12px;
  font-size: 13px;
  color: var(--color-text-secondary);
}

.mae-key {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.mae-sw {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  flex-shrink: 0;
}

.mae-sw-latent { background: #8bbd50; }
.mae-sw-joint { background: #77acee; }

body.dark-mode .mae-sw-latent { background: #c3f98a; }
body.dark-mode .mae-sw-joint { background: #77acee; }

@media (max-width: 768px) {
  .barchart .val,
  .barchart .cat {
    font-size: 17px;
  }

  .barchart .band {
    font-size: 15px;
  }

  .barchart .axis-note {
    font-size: 13px;
  }
}

/* Below ~700px the wide column chart would shrink its labels past legibility, so
   the columns turn into rows: the same seven values in a portrait viewBox that
   fits the screen at readable type, with no sideways scroll. Exactly one of the
   two is rendered, so screen readers and print see a single chart. */
.chart-narrow {
  display: none;
}

@media (max-width: 700px) {
  .chart-wide {
    display: none;
  }

  /* Cap it at its authored width: stretched to a 640px card the rows would
     render as 33px slabs with oversized type. Below 420px it scales down. */
  .chart-narrow {
    display: block;
    max-width: 420px;
    margin: 0 auto;
  }

  /* Rows are set in a 420-unit-wide viewBox, so they scale up on screen -- keep
     the type at its authored size rather than the wide chart's mobile bump. */
  .chart-narrow .val,
  .chart-narrow .cat {
    font-size: 15px;
  }

  .chart-narrow .ytick text,
  .chart-narrow .axis-title {
    font-size: 13px;
  }
}

/* Small phones scale the 420-unit viewBox down to ~290px, so the type is
   authored larger to land back at a readable size. */
@media (max-width: 430px) {
  .chart-narrow .val,
  .chart-narrow .cat {
    font-size: 18px;
  }

  .chart-narrow .band,
  .chart-narrow .ytick text,
  .chart-narrow .axis-title {
    font-size: 15px;
  }
}

/* ===== Real-robot video =====
   One plain <video> with native controls rather than the scripted rollout player:
   there is a single clip to show, nothing to synchronise against, and the native
   element gives fullscreen and scrubbing for free. */
.real-frame {
  padding: 0;                 /* the frame is the video's own bezel here */
  background: #000;           /* letterboxing reads as intentional, not as a gap */
}

.real-video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;       /* holds the layout before the poster loads */
  background: #000;
}

/* ===== Rollout demos =====
   Three embodiments side by side on one shared timeline. They are not toggled:
   the clips of a motion are cut to the same frames under one crop, so the
   comparison is meant to be read across the row at a glance.

   The figure runs the full text column rather than the 820px the other figures
   use, since three panels split across it and each one wants the room. */
.demo {
  width: 100%;
}

/* The controls do nothing until the script wires them up, and the motion picker
   is empty until then, so they stay hidden rather than showing as dead chrome.
   Without JS the figure is simply absent. */
.demo-bar,
.demo-foot {
  visibility: hidden;
}

.demo.is-ready .demo-bar,
.demo.is-ready .demo-foot {
  visibility: visible;
}

.demo-bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

/* Motions change the timeline; there is nothing else to pick, since every body
   is on screen already. */
.demo-motions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.demo-motion {
  padding: 4px 13px;
  border: 1px solid transparent;
  border-radius: 980px;
  background: none;
  font: inherit;
  font-size: 13px;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast);
}

.demo-motion:hover {
  color: var(--color-text);
}

.demo-motion[aria-pressed="true"] {
  background: var(--color-border);
  color: var(--color-text);
}

.demo-motion:focus-visible,
.demo-play:focus-visible,
.demo-track:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.demo-play {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border: 1px solid var(--color-border);
  border-radius: 980px;
  background: none;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.demo-play:hover {
  border-color: var(--color-text-secondary);
  color: var(--color-text);
}

.demo-play svg { fill: currentColor; }

/* The button shows the action it performs, so the pause bars are up while it
   plays and the play triangle only while it is stopped. */
.demo-play .ic-play { display: none; }
.demo.is-paused .demo-play .ic-pause { display: none; }
.demo.is-paused .demo-play .ic-play { display: inline; }

.demo-stage {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.demo-cell {
  position: relative;
  border: 1px solid var(--color-border-light);
  border-radius: 10px;
  overflow: hidden;
  background: var(--color-card-bg);
}

body.dark-mode .demo-cell {
  border-color: var(--color-border);
}

.demo-vid {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

/* The label rides on the clip rather than under it: stacked on a phone, three
   captions of their own would cost the height that lets all three fit at once.
   The backdrop is the same light render in either theme, so the pill does not
   follow the site's colours. */
.demo-cap {
  position: absolute;
  top: 7px;
  left: 7px;
  padding: 2px 8px;
  border-radius: 980px;
  background: rgba(255, 255, 255, 0.82);
  color: #1d1d1f;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.01em;
  pointer-events: none;
}

.demo-cap.is-placeholder {
  font-weight: 500;
  background: rgba(255, 255, 255, 0.9);
  border: 1px dashed rgba(29, 29, 31, 0.45);
  pointer-events: auto;
}

.demo-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 9px;
}

.demo-track {
  position: relative;
  flex: 1;
  height: 4px;
  border-radius: 980px;
  background: var(--color-border);
  cursor: pointer;
}

.demo-fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: 980px;
  background: var(--color-text-secondary);
}

.demo-time {
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  font-size: 11px;
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}

/* A tap target of 4px is unusable on a phone, so the bar keeps its look and
   grows an invisible one. */
@media (hover: none) {
  .demo-track::after {
    content: '';
    position: absolute;
    inset: -12px 0;
  }
}

/* Phones: the row becomes a column, and the clips are sized off the viewport
   height rather than the width so that all three are on screen together --
   comparing them is the point, and a column you have to scroll to reach is not
   being compared.

   The height is what is left of the screen after the picker, the scrubber and
   the gaps, split three ways, so the block fits a short phone as well as a tall
   one. dvh where it is supported, since a collapsing address bar changes vh
   under you mid-scroll. The clamp keeps it legible on a very short screen and
   stops it ballooning on a tall narrow window. */
@media (max-width: 620px) {
  .demo {
    max-width: none;
  }

  .demo-stage {
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 6px;
  }

  .demo-cell {
    width: auto;
    max-width: 100%;
  }

  .demo-vid {
    height: clamp(118px, calc((100vh - 160px) / 3), 260px);
    height: clamp(118px, calc((100dvh - 160px) / 3), 260px);
    width: auto;
    max-width: 100%;
  }

  .demo-motion {
    padding: 7px 12px;
    font-size: 12px;
    line-height: 1.25;
  }

  .demo-cap {
    top: 5px;
    left: 5px;
    font-size: 11px;
  }

  .demo-play {
    width: 34px;
    height: 34px;
  }

  .demo-foot {
    margin-top: 10px;
  }
}

/* ===== Interactive latent map =====
   Three t-SNE panels drawn from the exported trajectories. Hovering one motion
   highlights it in every panel; everything else recedes. */
.latent-map {
  position: relative;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border-light);
  border-radius: 12px;
  padding: 18px 18px 14px;
}

body.dark-mode .latent-map {
  border-color: var(--color-border);
}

.latent-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 4px 8px;
  margin-bottom: 12px;
}

.latent-group {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px;
}

.latent-divider {
  width: 1px;
  height: 18px;
  background: var(--color-border);
  margin: 0 4px;
}

.latent-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 4px 11px;
  border: 1px solid var(--color-border);
  border-radius: 980px;
  background: none;
  font: inherit;
  font-size: 13px;
  color: var(--color-text);
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast),
    background var(--transition-fast), opacity var(--transition-fast);
}

.latent-chip:hover {
  border-color: var(--color-text-secondary);
}

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

/* Deselected: the swatch drains and the label recedes, so "off" is legible
   without relying on color alone. */
.latent-chip[aria-pressed="false"] {
  color: var(--color-text-secondary);
  border-color: transparent;
  opacity: 0.55;
}

.latent-chip[aria-pressed="false"] i {
  background: var(--color-text-secondary) !important;
  opacity: 0.45;
}

.latent-chip i {
  width: 15px;
  height: 3px;
  border-radius: 2px;
  flex-shrink: 0;
}

.latent-chip i.sw-src { background: #6b6b6b; }
.latent-chip i.sw-tgt { background: #b9b9b9; }

body.dark-mode .latent-chip i.sw-src { background: #9a9a9a; }
body.dark-mode .latent-chip i.sw-tgt { background: #e2e2e2; }

.latent-reset {
  padding: 4px 12px;
  border: 1px solid var(--color-accent);
  border-radius: 980px;
  background: none;
  font: inherit;
  font-size: 13px;
  color: var(--color-accent);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.latent-reset:hover {
  background: var(--color-accent);
  color: #fff;
}

/* The dark-mode accent is light, so ink on it rather than white. */
body.dark-mode .latent-reset:hover {
  color: #000;
}

/* Nothing is filtered: the button goes invisible but keeps its slot, so the
   chips beside it never shift when a filter is applied or cleared. */
.latent-reset.is-idle {
  visibility: hidden;
  pointer-events: none;
}

/* Filtered out entirely — not just dimmed, so it leaves no hover target. */
.latent-map .is-off {
  display: none;
}

.latent-panels {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.latent-panel {
  margin: 0;
  min-width: 0;
}

.latent-panel figcaption {
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-secondary);
  margin: 0 0 2px;
}

.latent-svg {
  display: block;
  width: 100%;
  height: auto;
  touch-action: pan-y;
}

.latent-frame {
  fill: none;
  stroke: var(--color-border);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.latent-trace {
  fill: none;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  stroke-linecap: round;
  transition: opacity var(--transition-fast);
}

.latent-trace.src-g1 {
  stroke-width: 1;
  opacity: 0.75;
}

.latent-trace.src-target {
  stroke-width: 1.6;
  opacity: 0.95;
}

.latent-hull {
  fill: none;
  stroke: var(--color-text);
  stroke-width: 1.2;
  stroke-dasharray: 5 4;
  opacity: 0.55;
  vector-effect: non-scaling-stroke;
  transition: opacity var(--transition-fast);
}

/* Invisible fat stroke: the hover target for a hairline curve. */
.latent-hit {
  fill: none;
  stroke: transparent;
  stroke-width: 11;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* Recede everything that is not the hovered motion (or hovered behavior). */
.latent-map.is-hovering .latent-motion:not(.is-active) .latent-trace,
.latent-map.is-hovering .latent-hull:not(.is-active) {
  opacity: 0.12;
}

.latent-map.is-hovering .latent-motion.is-active .latent-trace {
  opacity: 1;
}

.latent-map.is-hovering .latent-motion.is-active .latent-trace.src-target {
  stroke-width: 2.4;
}

.latent-tip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  padding: 7px 11px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
  font-size: 13px;
  line-height: 1.45;
  white-space: nowrap;
}

.latent-tip strong {
  display: block;
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  font-size: 12.5px;
  color: var(--color-text);
  font-weight: 600;
}

.latent-tip span {
  color: var(--color-text-secondary);
}

.latent-keys {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 20px;
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--color-text-secondary);
}

.lk {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.lk line {
  stroke-width: 2;
  stroke-linecap: round;
}

.key-src { stroke: #6b6b6b; }
.key-tgt { stroke: #b9b9b9; }
.key-ood { stroke: currentColor; stroke-dasharray: 5 4; }

body.dark-mode .key-src { stroke: #9a9a9a; }
body.dark-mode .key-tgt { stroke: #e2e2e2; }

.latent-hint {
  opacity: 0.8;
  font-style: italic;
}

@media (max-width: 860px) {
  .latent-divider {
    display: none;
  }

  .latent-panels {
    grid-template-columns: 1fr;
    gap: 18px;
    max-width: 420px;
    margin: 0 auto;
  }

  .latent-hint {
    display: none;
  }
}

.latent-map noscript img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 8px;
}

/* ===== Joint map (embodiment setup) =====
   Four columns, each robot's actuated joints in model index order. Two hues only
   -- categorical slots 1 and 2 -- because any two marks can end up adjacent here;
   both clear all-pairs CVD, normal-vision and 3:1 contrast on the card in light
   and dark. Body group rides in the run headings, not in a third hue, and every
   mark is directly labelled, so color never carries identity alone. */
.jointmap {
  background: var(--color-card-bg);
  border: 1px solid var(--color-border-light);
  border-radius: 12px;
  padding: 18px 18px 14px;
}

body.dark-mode .jointmap {
  border-color: var(--color-border);
}

.jm-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 18px;
  margin-bottom: 16px;
  font-size: 13px;
  color: var(--color-text-secondary);
}

.jm-key {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.jm-sw {
  width: 4px;
  height: 13px;
  border-radius: 2px;
  flex-shrink: 0;
  background: #2a78d6;
}

.jm-key .jm-sw {
  height: 11px;
}

.jm-j.is-only .jm-sw,
.jm-key .jm-sw.jm-only {
  background: #eb6834;
}

body.dark-mode .jm-sw { background: #3987e5; }
body.dark-mode .jm-j.is-only .jm-sw,
body.dark-mode .jm-key .jm-sw.jm-only { background: #d95926; }

.jm-cols {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px 12px;
  align-items: start;
}

.jm-col {
  min-width: 0;
}

.jm-head {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--color-border);
}

.jm-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text);
}

.jm-meta {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-secondary);
}

/* Kinematic summary: D, B and the observation dimension 2D + 8 + 3B, which is
   what the removed setup table used to carry. */
.jm-nums {
  margin-top: 3px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--color-text-secondary);
}

.jm-nums b {
  font-weight: 600;
  color: var(--color-text);
}

.jm-nums i {
  font-style: normal;
  opacity: 0.5;
}

.jm-morph {
  margin-top: 3px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--color-text-secondary);
  opacity: 0.85;
}

/* Run heading: the group changes here, which is the block structure the
   "legs-first" / "head-first" descriptions in the caption refer to. */
.jm-grp {
  margin: 10px 0 3px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  opacity: 0.75;
}

.jm-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.jm-j {
  display: grid;
  grid-template-columns: 4px 17px 1fr;
  align-items: center;
  gap: 6px;
  padding: 2px 4px 2px 0;
  border-radius: 4px;
  cursor: pointer;
  transition: opacity var(--transition-fast), background var(--transition-fast);
}

.jm-i {
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  font-size: 10px;
  text-align: right;
  color: var(--color-text-secondary);
  opacity: 0.7;
}

.jm-lab {
  font-size: 12px;
  line-height: 1.45;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Hovering one joint recedes everything that is not the same joint, so the eye
   lands on its position in the other three columns. */
.jm-cols.is-hovering .jm-j {
  opacity: 0.3;
}

.jm-cols.is-hovering .jm-j.is-hit {
  opacity: 1;
  background: var(--color-bg-alt);
}

body.dark-mode .jm-cols.is-hovering .jm-j.is-hit {
  background: #2f2f2f;
}

.jm-readout {
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--color-border-light);
  font-size: 13px;
  line-height: 1.5;
  text-align: center;
  color: var(--color-text-secondary);
}

.jm-readout strong {
  color: var(--color-text);
  margin-right: 8px;
}

.jm-readout b {
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
  font-weight: 600;
  color: var(--color-text);
}

.jm-absent {
  opacity: 0.55;
}

body.dark-mode .jointmap .jm-readout {
  border-top-color: var(--color-border);
}

/* Two columns rather than four below ~880px: four columns of joint names stop
   fitting, and the alternative is a sideways scroll. */
@media (max-width: 880px) {
  .jm-cols {
    grid-template-columns: repeat(2, 1fr);
    gap: 18px 14px;
  }
}

/* Phones: even two columns of four robots run to several screens, so the map
   shows one robot at a time behind a picker and flows that robot's joints into
   two columns. Above this width all four columns stay up -- comparing the
   orderings side by side is the whole point of the figure. */
.jm-picker {
  display: none;
}

@media (max-width: 620px) {
  .jm-picker {
    display: flex;
    gap: 6px;
    margin-bottom: 14px;
  }

  .jm-pick {
    flex: 1 1 0;
    min-width: 0;
    padding: 6px 4px;
    border: 1px solid var(--color-border);
    border-radius: 980px;
    background: none;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: color var(--transition-fast), border-color var(--transition-fast),
      background var(--transition-fast);
  }

  .jm-pick[aria-pressed="true"] {
    border-color: var(--color-text);
    background: var(--color-text);
    color: var(--color-card-bg);
  }

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

  .jm-cols {
    grid-template-columns: 1fr;
  }

  /* Display, not removal: the other three stay in the DOM so hovering a joint
     can still report its index on every body. */
  .jm-col {
    display: none;
  }

  .jm-col.is-active {
    display: block;
  }

  .jm-body {
    columns: 2;
    column-gap: 16px;
  }

  /* A run heading must never be the last thing in a column, and a joint must
     not be split down the middle by the column break. */
  .jm-grp {
    break-after: avoid;
  }

  .jm-j {
    break-inside: avoid;
  }
}

@media (max-width: 400px) {
  .jm-lab {
    font-size: 11px;
  }

  .jm-j {
    grid-template-columns: 4px 15px 1fr;
    gap: 5px;
  }
}

/* ===== Objective cards (Stage 1, deployment) =====
   One card carrying an equation and the sentence that reads it — the alignment
   loss in Stage 1, the reward projection at deployment. */
.loss-anatomy {
  margin: 30px 0 8px;
}

.loss-sum {
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  padding: 16px 18px 14px;
  text-align: center;
  container-type: inline-size;
}

body.dark-mode .loss-sum {
  border-color: var(--color-text-secondary);
}

/* The equation sizes itself from the card rather than the viewport: `.eq` is
   white-space: nowrap and cannot reflow, so the font size is what has to give.
   The px value first is the fallback where cqw is unsupported, and the scroller
   is the backstop for the narrowest phones.

   5cqw sets the longest equation to ~80% of the card width, which is what phones
   get: there the cap is far out of reach and the type scales with the card. The
   cap binds from about a 600px card upward, so desktop still reads at 30px and
   is unaffected by this ratio — it only governs how small screens behave. */
.loss-sum .eq {
  font-size: 17px;
  font-size: clamp(15px, 5cqw, 30px);
  line-height: 1.7;
  margin: 6px 0 12px;
  overflow-x: auto;
}

/* Continuation line — the definition that follows the objective it appears in.
   Held at 80% of the objective's ratio so it stays visibly subordinate at every
   width rather than crowding the line it qualifies. */
.loss-sum .eq-sub {
  font-size: 14px;
  font-size: clamp(13px, 4cqw, 24px);
  margin-top: -6px;
}

.loss-note {
  font-size: 14px;
  line-height: 1.6;
  color: var(--color-text-secondary);
  margin: 0;
}

.loss-note strong {
  color: var(--color-text);
}

/* The prose under the equation is a paragraph, not a caption: centring it the
   way the equation is centred would leave it ragged on both edges. */
.loss-solo .loss-note {
  text-align: left;
}

/* ===== Stages 2-3, side by side ===== */
.stage-pair {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 26px;
  margin-top: 6px;
}

.stage-col {
  min-width: 0;
}

.stage-col .fig-frame {
  padding: 10px;
}

.stage-role {
  margin: 14px 0 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.stage-col p:not(.stage-role) {
  font-size: 15px;
  line-height: 1.65;
}

@media (max-width: 760px) {
  .stage-pair {
    grid-template-columns: 1fr;
    gap: 30px;
  }
}

/* The two stage figures have slightly different aspect ratios; a common box keeps
   their captions on one line. The frame is white, so the letterboxing is invisible. */
.stage-col .fig-frame img {
  aspect-ratio: 2.6 / 1;
  object-fit: contain;
}
