/* =================================================================
   Style.css — adapted from VideoMimic's structure with a custom
   visual identity:
     - cooler near-white background (#fafafa instead of #fdfaf4)
     - Inter / IBM Plex Sans for tech-paper feel
     - JetBrains Mono for code/captions/TOC
     - belief-grid orange-red accent (#e85d3a) used sparingly
     - hairline 1px borders, no rounded corners on figures
   ================================================================= */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');

:root {
  --bg:           #fafafa;
  --ink:          #111111;
  --ink-soft:     #444444;
  --ink-faint:    #777777;
  --rule:         #1a1a1a;
  --rule-faint:   #d8d8d8;
  --accent:       #e85d3a;   /* belief-grid orange-red */
  --accent-soft:  #f7d4c8;
  --bar-baseline: #b8b8b8;
  --bar-ablation: #555555;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  background-color: var(--bg);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  color: var(--ink);
  padding: 60px 30px 30px;
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;   /* never show a horizontal scrollbar / shifted view */
}

/* =================================================================
   TABLE OF CONTENTS (toggle button + overlay panel)

   One form at every width, instead of the old left rail that needed a
   ≥1640px viewport to sit beside the 1150px column and so never showed
   up on a laptop. A hairline square button pins to the top-right and
   fades in once the hero title scrolls off; clicking it drops a panel
   that overlays the page. Both are `position: fixed` with matching
   right offsets, so the panel hangs directly under the button.
   ================================================================= */
.toc-toggle {
  position: fixed;
  top: 22px;
  right: 24px;
  z-index: 1001;
  width: 40px;
  height: 40px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(250, 250, 250, 0.92);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid var(--rule);
  color: var(--ink);
  cursor: pointer;
  /* Hidden until the hero scrolls past. `visibility` (not just opacity)
     keeps it out of the tab order while it's faded out. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s,
              color 0.15s ease, border-color 0.15s ease;
}

.toc-toggle.is-visible {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.toc-toggle:hover {
  color: var(--accent);
  border-color: var(--accent);
}

/* Inverted while the panel is open, so the button reads as pressed. */
.toc-toggle.is-open {
  background-color: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}

.toc {
  width: 232px;
  font-family: 'JetBrains Mono', monospace;
  position: fixed;
  top: 74px;
  right: 24px;
  z-index: 1000;
  max-height: calc(100vh - 100px);
  overflow-y: auto;
  padding: 16px 0 12px;
  background-color: rgba(250, 250, 250, 0.94);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid var(--rule);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(-6px);
  transition: opacity 0.22s ease, transform 0.22s ease, visibility 0.22s;
}

.toc.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: none;
}

.toc h3 {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin: 0 16px 8px;
  color: var(--ink);
}

.toc hr {
  border: 0;
  height: 1px;
  background-color: var(--rule);
  width: 32px;     /* short rule, more editorial */
  margin: 0 16px 10px;
}

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

.toc li {
  margin: 0;
}

/* Full-width rows: the whole strip is the hit target, and the left edge
   carries the active marker. */
.toc a {
  display: block;
  padding: 6px 16px;
  border-left: 2px solid transparent;
  text-decoration: none;
  color: var(--ink-soft);
  font-size: 13px;
  letter-spacing: 0.02em;
  line-height: 1.45;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.toc a:hover {
  color: var(--accent);
  border-left-color: var(--accent-soft);
}

/* Set by the scroll-spy in main.js on the section you're reading. */
.toc a.is-active {
  color: var(--accent);
  border-left-color: var(--accent);
}

.toc-subsection a {
  padding-left: 30px;
  font-size: 12px;
  color: var(--ink-faint);
}

/* =================================================================
   MAIN CONTENT
   ================================================================= */
.main-content {
  max-width: 1150px;
  margin: 0 auto;
}

/* ---------- Hero ---------- */
.hero-text {
  font-family: 'Inter', sans-serif;
  font-size: 50px;
  font-weight: 800;
  letter-spacing: -0.025em;
  line-height: 1.05;
  margin-bottom: 24px;
  color: var(--ink);
}

.hero-accent {
  color: var(--accent);
}
/* Acronym source letters: HuG = Humanoid + Grasping.
   Accent + underline only these letters so the title spells the acronym. */
.hug-mark {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 3px;
  text-underline-offset: 6px;
}
/* The HuG name itself, slightly larger than the rest of the title. */
.hug-name {
  font-size: 1.12em;
}

.sub-hero-text {
  font-family: 'Inter', sans-serif;
  font-size: 22px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--ink-soft);
  margin-bottom: 36px;
  max-width: 720px;
}

/* ---------- Authors ---------- */
.authors {
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
  color: var(--ink-soft);
  line-height: 1.6;
  margin-bottom: 20px;
  text-align: left;
}
.authors .author-list {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
}
.authors .author-list sup {
  font-size: 11px;
  font-weight: 600;
  vertical-align: super;
  line-height: 0;
}
.authors .author-note {
  display: block;
  font-size: 13px;
  color: var(--ink-soft);
  margin-top: 4px;
}
.authors .affiliation {
  display: block;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  margin-top: 2px;
}
/* Venue: a second accent beat under the title. The short form is set in
   the title's own Inter 800 voice; the full name stays in the mono voice
   of the author block so it reads as a quiet companion, not a repeat. */
.authors .venue {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  margin-top: 14px;
}
.authors .venue-tag {
  font-family: 'Inter', sans-serif;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--accent);
}
.authors .venue-name {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-faint);
}
.authors a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-faint);
}
.authors a:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}


/* ---------- Scroll hero film ----------
   Full-bleed, full-viewport film above the title, after the ASENA site.
   main.js writes --hero-p (0 at the top, 1 once the first ~70% of a
   viewport has scrolled by); the frame scales down toward the text column
   and rounds its corners as it does, while the wordmark and scroll cue
   fade out. Only transform/opacity change, so the layout never moves. */
.hero-film {
  --hero-p: 0;
  position: relative;
  height: 100vh;
  height: 100svh;
  /* Cancel the body padding so the film touches every viewport edge. */
  margin: -60px -30px 56px;
}

.hero-film-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #000;
  transform-origin: center 30%;
  transform: scale(calc(1 - var(--hero-p) * var(--hero-shrink, 0.14)));
  border-radius: calc(var(--hero-p) * 22px);
  will-change: transform;
}

.hero-film-frame video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-film-shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0) 40%);
  pointer-events: none;
}

.hero-film-wordmark {
  position: absolute;
  left: 40px;
  bottom: 28px;
  font-family: 'Inter', sans-serif;
  font-size: clamp(64px, 12vw, 168px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1;
  color: #fafafa;
  opacity: calc(0.92 - var(--hero-p) * 0.92);
  pointer-events: none;
}

.hero-film-scroll {
  position: absolute;
  right: 40px;
  bottom: 40px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fafafa;
  text-decoration: none;
  opacity: calc(1 - var(--hero-p) * 2);
}
.hero-film-scroll span {
  display: inline-block;
  animation: hero-film-bob 1.6s ease-in-out infinite;
}
@keyframes hero-film-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(4px); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-film-frame { transform: none; border-radius: 0; }
  .hero-film-scroll span { animation: none; }
}

/* ---------- Hero teaser video ---------- */
.hero-video-wrapper {
  margin-bottom: 8px;
  border: 1px solid var(--rule);
  background-color: #000;
}

#teaser-video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

/* ---------- Quick links — plain monospace [pdf] / [arxiv] / [code] ---------- */
/* Paper links + the one call to action, as one row of square mono
   buttons: 1 px ink rule like the image frames, uppercase tracked mono
   like the taglines and the "scroll to explore" label. The live-demo
   button is the same shape filled with the accent; both invert on hover. */
.quick-links {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 10px;
  margin: 0 0 26px;
}
.quick-links a {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 10px 16px;
  border: 1px solid var(--rule);
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--ink);
  background: transparent;
  text-decoration: none;
  transition: color 0.15s ease, background 0.15s ease;
}
.quick-links a:hover,
.quick-links a:focus-visible {
  color: var(--bg);
  background: var(--ink);
}
.quick-links a.demo-button {
  color: #fafafa;
  background: var(--accent);
  border-color: var(--accent);
}
.quick-links a.demo-button:hover,
.quick-links a.demo-button:focus-visible {
  color: var(--accent);
  background: var(--bg);
}
.demo-button-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  animation: demo-dot-pulse 2.2s ease-out infinite;
}
@keyframes demo-dot-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(250, 250, 250, 0.55); }
  70%  { box-shadow: 0 0 0 6px rgba(250, 250, 250, 0); }
  100% { box-shadow: 0 0 0 0 rgba(250, 250, 250, 0); }
}
.quick-links a.demo-button:hover .demo-button-dot,
.quick-links a.demo-button:focus-visible .demo-button-dot {
  animation: none;
}
@media (prefers-reduced-motion: reduce) {
  .demo-button-dot { animation: none; }
  .quick-links a { transition: none; }
}

/* =================================================================
   SECTION HEADINGS — "tagline" (top-level) and "section-subtitle"
   Following VideoMimic's pattern of black-block ribbons,
   but flipped to be more restrained — a thin horizontal rule plus
   monospace label, no inverted color block.
   ================================================================= */
.tagline {
  font-family: 'JetBrains Mono', monospace;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  display: block;
  margin: 64px 0 28px;
  padding-bottom: 10px;
  border-bottom: 1.5px solid var(--ink);
}

/* The Interactive Demo heading is the one place on the page that moves on
   its own: the letters sit ink-black, then a rainbow band wipes across
   them left-to-right once every 2.6 seconds. No JS — it's a single
   gradient three times wider than the heading, ink at both ends with a
   narrow spectrum band in the middle third, slid from right to left so
   the band appears to travel rightward. The band is kept inside the
   middle third (42%-58%) so that at both ends of the slide the visible
   window is pure ink and the loop restart is invisible. */
.tagline--rainbow {
  background-image: linear-gradient(100deg,
      var(--ink) 0%, var(--ink) 36%,
      #e0402a 42%, #ef9a12 46%, #e3ce18 50%,
      #29a24a 54%, #2a6fe0 58%,
      var(--ink) 64%, var(--ink) 100%);
  background-size: 300% 100%;
  background-repeat: no-repeat;
  background-position: 0 0;
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .tagline--rainbow {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    animation: tagline-rainbow-sweep 2.6s linear infinite;
  }
}

/* One uninterrupted slide, no holds: at both ends the visible window is
   pure ink anyway, so a pause there would only lengthen the dark gap
   between passes. */
@keyframes tagline-rainbow-sweep {
  from { background-position: 100% 0; }
  to   { background-position: 0% 0; }
}

/* Decoration only — drop it entirely rather than freeze mid-sweep, which
   would leave the heading half-colored. */
@media (prefers-reduced-motion: reduce) {
  .tagline--rainbow {
    background-image: none;
    animation: none;
    color: var(--ink);
    -webkit-text-fill-color: var(--ink);
  }
}

.section-subtitle {
  font-family: 'Inter', sans-serif;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 36px 0 18px;
  padding-left: 14px;
  border-left: 3px solid var(--accent);
}

/* =================================================================
   LIVE DEMO — click-to-launch poster (the simulator is NOT embedded;
   see the note in index.html). The whole frame is one link wrapping a
   looping capture of the demo, framed like every other figure here:
   hairline border, square corners, no gradients.
   ================================================================= */
.live-demo-frame {
  display: block;
  position: relative;
  margin: 0 0 12px;
  border: 1px solid var(--rule);
  /* The capture's own ratio — the page's usual 16/9 would crop the HUD. */
  aspect-ratio: 1536 / 654;
  /* The demo page's own background, so first paint and any rounding gap
     read as part of the picture rather than a dark flash. */
  background: #eef2f7;
  overflow: hidden;
  text-decoration: none;
  /* Hover lifts the whole door 10% toward the reader; z-index keeps the
     enlarged frame above the caption and heading it now overlaps. */
  transition: transform 0.35s cubic-bezier(.2, .7, .2, 1), box-shadow 0.35s ease;
}

.live-demo-frame:hover,
.live-demo-frame:focus-visible {
  transform: scale(1.1);
  z-index: 2;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.18);
}

.live-demo-frame video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Bottom-left: the one corner the capture keeps clear start to finish
   (HUD sits top-left, robot center-right). A solid accent block, always
   visible — the capture is light and busy, so a restrained outline button
   disappears into it, and this frame has to advertise that it is a door,
   not another result clip. */
.live-demo-cta {
  position: absolute;
  left: 20px;
  bottom: 20px;
  max-width: calc(100% - 40px);
  font-family: 'Inter', sans-serif;
  font-size: 20px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.01em;
  color: #fafafa;
  background: var(--accent);
  padding: 16px 34px;
  transition: background 0.15s ease, transform 0.12s ease;
}

/* On hover the block turns paper-white and the label takes the same
   rainbow sweep as the Interactive Demo heading — on the orange block the
   spectrum would wash out, so the background flips to carry it. The
   gradient lives on the inner span: background-clip:text on the block
   itself would clip away the block's own fill. */
.live-demo-frame:hover .live-demo-cta,
.live-demo-frame:focus-visible .live-demo-cta {
  background: #fafafa;
  transform: translateY(-1px);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .live-demo-frame:hover .live-demo-cta-text,
  .live-demo-frame:focus-visible .live-demo-cta-text {
    background-image: linear-gradient(100deg,
        var(--ink) 0%, var(--ink) 36%,
        #e0402a 42%, #ef9a12 46%, #e3ce18 50%,
        #29a24a 54%, #2a6fe0 58%,
        var(--ink) 64%, var(--ink) 100%);
    background-size: 300% 100%;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    animation: tagline-rainbow-sweep 1.6s linear infinite;
  }
}

@media (prefers-reduced-motion: reduce) {
  .live-demo-frame,
  .live-demo-cta { transition: none; }
  .live-demo-frame:hover,
  .live-demo-frame:focus-visible { transform: none; }
  .live-demo-frame:hover .live-demo-cta-text,
  .live-demo-frame:focus-visible .live-demo-cta-text {
    animation: none;
    background-image: none;
    color: var(--accent);
    -webkit-text-fill-color: var(--accent);
  }
}

.live-demo-frame:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* =================================================================
   PROSE
   ================================================================= */
.section {
  font-size: 17px;
  line-height: 1.7;
  margin-bottom: 36px;
  color: var(--ink);
}

.section em {
  font-style: italic;
  color: var(--ink);
}

.section b {
  font-weight: 600;
}

.section code {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.92em;
  background: var(--accent-soft);
  color: var(--ink);
  padding: 1px 5px;
}

.smallcaps {
  font-variant: small-caps;
  letter-spacing: 0.04em;
  font-weight: 600;
}

/* =================================================================
   IMAGES (figures)
   ================================================================= */
img {
  width: 100%;
  display: block;
  border: 1px solid var(--rule);
  background: #fff;
  margin: 0;
  box-sizing: border-box;
}

a#figure-method-img {
  display: block;
  text-decoration: none;
  cursor: default;
  margin: 14px 0 0;
}
/* Method figure: no frame, no white padding — show the artwork itself. */
a#figure-method-img img {
  border: none;
  background: transparent;
}

/* =================================================================
   FIGURE CAPTIONS
   ================================================================= */
.figure-caption {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-soft);
  text-align: left;
  margin: 12px 0 36px;
  padding: 0;
}
.figure-caption b {
  color: var(--ink);
  font-weight: 700;
}
/* Inline links in body copy and captions. The page has no global `a` rule,
   so without this they render default-browser blue. The hairline underline
   firms up on hover rather than appearing on it, so the link never shifts
   the line it sits on. */
.section a,
.figure-caption a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid var(--accent-soft);
  transition: border-color 0.15s ease;
}
.section a:hover,
.section a:focus,
.figure-caption a:hover,
.figure-caption a:focus {
  border-bottom-color: var(--accent);
}

/* The demo intro belongs to the launch frame directly beneath it, so the
   pair reads as one unit: tighter than the page's default 28px/36px
   section rhythm. Scoped to this section — the global rhythm is unchanged. */
#live-demo { margin-bottom: 14px; }
.live-demo-intro { margin-bottom: 16px; }

/* =================================================================
   VIDEO GALLERY (horizontal scroll)
   ================================================================= */
.video-gallery-section {
  margin-bottom: 48px;
  width: 100vw;
  position: relative;
  margin-left: calc((100% - 100vw) / 2);
  box-sizing: border-box;
}

.video-gallery-container {
  box-sizing: border-box;
  max-width: 1200px;
  margin: 0 auto;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.video-gallery-container::-webkit-scrollbar { display: none; }

.video-gallery {
  display: flex;
  gap: 12px;
  padding: 0 20px 6px;
  box-sizing: border-box;
  width: max-content;
  margin: 0 auto;
}

.gallery-video {
  flex: 0 0 auto;
  height: 220px;
  width: auto;
  border: 1px solid var(--rule);
  background-color: #000;
  box-sizing: border-box;
}

/* Gallery navigation */
.gallery-nav {
  background-color: var(--ink);
  color: #fff;
  border: none;
  width: 32px;
  height: 32px;
  font-size: 16px;
  font-weight: 600;
  font-family: 'JetBrains Mono', monospace;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.15s ease;
  margin: 0 4px;
  flex-shrink: 0;
}
.gallery-nav:hover {
  background-color: var(--accent);
}

.gallery-nav-controls {
  display: flex;
  align-items: center;
  margin-right: 16px;
}

.gallery-caption-container {
  max-width: 960px;
  margin: 14px auto 0;
  padding: 0 20px;
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

.figure-caption.gallery-caption {
  margin: 0;
  text-align: left;
  flex-grow: 1;
}

/* =================================================================
   PAIRED-VIDEO BLOCK (FSM Search/Inspect, etc.)
   Adapted from VideoMimic's r2s-vertical-layout
   ================================================================= */
.r2s-vertical-layout {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 12px 0 32px;
  box-sizing: border-box;
}

.r2s-video-row {
  display: flex;
  gap: 12px;
  align-items: stretch;
  justify-content: center;
}

.r2s-video-row > .r2s-video-item {
  flex: 1 1 0;
  display: block;
  width: 100%;
  height: auto;
  max-height: 280px;
  object-fit: contain;
  background-color: #000;
  border: 1px solid var(--rule);
  box-sizing: border-box;
}

.r2s-caption-row {
  display: flex;
  gap: 12px;
  justify-content: center;
}

.r2s-caption-item {
  flex: 1 1 0;
  text-align: center;
  font-size: 12px;
  color: var(--ink-soft);
  font-family: 'JetBrains Mono', monospace;
  margin: 0;
  padding: 4px 0;
}

/* =================================================================
   ADDITIONAL VIDEO ROW (3-up grid for whole-body align demo)
   ================================================================= */
.additional-video-row {
  display: flex;
  gap: 12px;
  justify-content: center;
  align-items: stretch;
  margin: 18px 0 4px;
}
.additional-video-item {
  flex: 1 1 0;
  height: 220px;
  width: 100%;
  object-fit: cover;
  border: 1px solid var(--rule);
  background-color: #000;
}

/* =================================================================
   ROLLOUT GRID (2x2, used for representative rollouts)
   ================================================================= */
.rollout-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin: 18px 0 12px;
}
.rollout-cell {
  display: flex;
  flex-direction: column;
}
.rollout-cell video {
  width: 100%;
  height: 240px;
  object-fit: cover;
  border: 1px solid var(--rule);
  background: #000;
  display: block;
}
.rollout-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  color: var(--ink-soft);
  margin: 8px 0 0;
  text-align: center;
}

/* =================================================================
   SCENE GRID  (continuous whole-body grasping across scenes)
   2-column gallery of scene tiles, label above each clip.
   ================================================================= */
.scene-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px 20px;
  margin: 22px 0 12px;
}
.scene-cell {
  display: flex;
  flex-direction: column;
}
.scene-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 7px;
  letter-spacing: 0.01em;
}
.scene-cell video {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--rule);
  background: #000;
  display: block;
}

/* Featured single clip (shelf hero, 9-object run, height).
   Capped narrower than the column and centered so it reads as a focused
   showcase rather than filling the full width. 16:9 to match real video. */
.feature-video-wrapper {
  max-width: 720px;
  margin: 18px auto 8px;
}
.feature-video-wrapper video {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--rule);
  background: #000;
  display: block;
}
/* Caption under a capped feature video: match its width and center. */
.figure-caption.feature-caption {
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

@media (max-width: 768px) {
  .scene-grid { grid-template-columns: 1fr; }
}

/* =================================================================
   LANGUAGE-GUIDED PAIR
   The ordered target plan (a still from a second camera on the same
   run, with the queried objects boxed) beside the execution clip.
   Both panels are 16:9, so the two frames end up the same height; a
   single caption under the row covers the pair. The active box and the
   progress list are driven from the clip's currentTime — see the sync
   script in index.html.
   ================================================================= */
.lang-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-items: start;
  margin: 18px 0 8px;
}
.lang-panel { margin: 0; }

.lang-panel video {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 1px solid var(--rule);
  background: #000;
  display: block;
}

.lang-plan-frame {
  position: relative;
  border: 1px solid var(--rule);
  background: #000;
}
.lang-plan-frame img { border: 0; }

.lang-plan-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Boxes: dashed white = candidate, solid accent = current target,
   thin accent = already grasped. Only the active box carries a label. */
.lang-box rect {
  fill: none;
  stroke: #fff;
  stroke-width: 2.5;
  stroke-dasharray: 8 7;
  opacity: 0.6;
  transition: opacity 0.25s ease;
}
.lang-box text {
  font-family: 'JetBrains Mono', monospace;
  font-size: 30px;
  font-weight: 700;
  fill: var(--accent);
  paint-order: stroke;             /* dark halo, so it reads over the wood */
  stroke: rgba(0, 0, 0, 0.7);
  stroke-width: 5px;
  stroke-linejoin: round;
  opacity: 0;
  transition: opacity 0.25s ease;
}
.lang-box.is-done rect {
  stroke: var(--accent);
  stroke-dasharray: none;
  stroke-width: 3;
  opacity: 0.45;
}
.lang-box.is-active rect {
  stroke: var(--accent);
  stroke-dasharray: none;
  stroke-width: 5.5;
  opacity: 1;
}
.lang-box.is-active text { opacity: 1; }

/* The plan, as a strip under the frame rather than a panel over the photo:
   the crop that declutters the image leaves no empty space to park it in,
   and out here the text stays crisp at a fixed size. */
.lang-plan-strip {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 3px 15px;
  margin: 9px 0 0;
  padding: 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-faint);
}
.lang-plan-strip .lang-plan-item {
  white-space: nowrap;
  transition: color 0.25s ease;
}
.lang-plan-strip .lang-mark {
  display: inline-block;
  width: 1.1em;                    /* fixed slot: marks change, widths don't */
  color: var(--accent);
}
.lang-plan-strip .lang-plan-item.is-done { color: #a2a2a2; }
.lang-plan-strip .lang-plan-item.is-active {
  color: var(--accent);
  font-weight: 700;
}

/* Stack on narrow screens, where two 16:9 panels would each be tiny. */
@media (max-width: 900px) {
  .lang-row { grid-template-columns: 1fr; gap: 26px; }
}

/* =================================================================
   TWO-COLUMN BLOCK (Technical Highlights)
   Each column is a self-contained highlight: subtitle, prose, clip.
   ================================================================= */
.highlight-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px 34px;
  align-items: start;
  margin-bottom: 40px;
}
.highlight-cols .section-subtitle { margin-top: 0; }
.highlight-cols .section {
  margin-bottom: 0;
  min-height: calc(3 * 1.7em);   /* three body lines, so the videos below stay level */
}

/* Workspace sweep under the highlight columns: 17 renders, 0.10-0.90 m.
   Deliberately quiet: no title, no frame. The JPEG's background is
   level-shifted to the page's --bg so it floats on the page; the scale
   row gives the range without axis labels in the image. */
.workspace-figure {
  margin: -8px 0 6px;
}
.workspace-figure img {
  display: block;
  width: 100%;
  border: 0;                 /* the global img rule frames every image */
  background: transparent;
}
.workspace-scale {
  display: flex;
  justify-content: space-between;
  padding: 2px 6px 0;
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
}
.workspace-scale span:nth-child(2) { text-transform: uppercase; }
@media (max-width: 720px) {
  /* Keep the sweep legible on phones: scroll it sideways instead of
     shrinking 17 frames into 360 px. */
  .workspace-figure { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .workspace-figure img, .workspace-scale { min-width: 960px; }
}
/* Clips inside a column: fill it, and left-align the caption to the text. */
.highlight-cols .feature-video-wrapper {
  max-width: 100%;
  margin: 16px 0 0;
}
.highlight-cols .figure-caption.feature-caption {
  max-width: 100%;
  margin: 10px 0 0;
  text-align: left;
}

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

/* =================================================================
   CAROUSEL (filmstrip of related clips, one centered)
   Every slide is the same width — the centered one shows in full and the
   neighbours are simply cropped by the viewport, so switching clips is a
   plain sideways pan of the strip. The neighbours also run shorter (their
   frame is vertically cropped) so the centered clip reads as the subject.
   Only the centered clip plays; the rest stay parked on their first frame.
   Arrows overlay the cropped edges.
   ================================================================= */
.carousel {
  --peek: 14%;                    /* how much of each neighbour shows */
  --gap: 14px;
  --side-height: 72%;             /* neighbour frame height, vs. the center */
  max-width: 1104px;
  margin: 24px auto 6px;
}

.carousel-stage { position: relative; }

.carousel-viewport { overflow: hidden; }

.carousel-track {
  display: flex;
  align-items: center;            /* short neighbours sit on the center line */
  gap: var(--gap);
  transition: transform 0.4s ease;
  will-change: transform;
}

.carousel-item {
  flex: 0 0 calc(100% - 2 * var(--peek) - 2 * var(--gap));
  aspect-ratio: 16 / 9;           /* holds the row's height, so nothing reflows */
  display: flex;
  align-items: center;
  margin: 0;
  opacity: 0.55;
  cursor: pointer;
  transition: opacity 0.4s ease;
}
.carousel-item:hover { opacity: 0.8; }
.carousel-item.is-center { opacity: 1; cursor: default; }

/* The frame is what shrinks: same width, less height, so the neighbour keeps
   the centered clip's image scale and is just cropped top and bottom. */
.carousel-frame {
  width: 100%;
  height: var(--side-height);
  overflow: hidden;
  border: 1px solid var(--rule);
  background: #000;
  transition: height 0.4s ease;
}
.carousel-item.is-center .carousel-frame { height: 100%; }

.carousel-item video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.carousel-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  text-align: center;
  margin: 9px 0 0;
  min-height: 1.6em;              /* hold the line so nothing jumps */
}

.carousel-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 34px;
  height: 34px;
  border: none;
  background-color: var(--ink);
  color: #fff;
  font-family: 'JetBrains Mono', monospace;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.15s ease, opacity 0.15s ease;
}
.carousel-nav.is-prev { left: 0; }
.carousel-nav.is-next { right: 0; }
.carousel-nav:hover { background-color: var(--accent); }

/* The clone→real hop after a wrap-around must be instant, everywhere. */
.carousel.is-hopping .carousel-track,
.carousel.is-hopping .carousel-item,
.carousel.is-hopping .carousel-frame { transition: none; }

/* Narrow screens: one clip at a time, no peeking neighbours. */
@media (max-width: 720px) {
  .carousel { --peek: 0%; --gap: 0px; }
}

/* =================================================================
   CHART BLOCK
   ================================================================= */
.chart-block {
  margin: 24px 0 36px;
  padding: 24px 20px 16px;
  border: 1px solid var(--rule);
  background-color: #fff;
}

.chart-title {
  font-family: 'Inter', sans-serif;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink);
  margin: 0 0 12px;
}

.bar-chart {
  width: 100%;
  height: auto;
  display: block;
}

.bar-chart .chart-tick {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  fill: var(--ink-faint);
}
.bar-chart .chart-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  fill: var(--ink);
}
.bar-chart .chart-sublabel {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  fill: var(--ink-faint);
}
.bar-chart .chart-label-ours {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  font-weight: 700;
  fill: var(--accent);
}
.bar-chart .chart-sublabel-ours {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  font-weight: 700;
  fill: var(--accent);
}
.bar-chart .chart-value {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  fill: var(--ink);
  font-weight: 500;
}
.bar-chart .chart-value-ours {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  fill: var(--accent);
  font-weight: 700;
}
.bar-chart .chart-section-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 10px;
  fill: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.bar-chart .chart-axis-title {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  fill: var(--ink-soft);
}
.bar-chart .bar-baseline { fill: var(--bar-baseline); }
.bar-chart .bar-ours     { fill: var(--accent); }
.bar-chart .bar-ablation { fill: var(--bar-ablation); }
.bar-chart .dot-accent   { fill: var(--accent); stroke: #fff; stroke-width: 1.5; }

/* =================================================================
   BIBTEX
   ================================================================= */
/* Light panel on the page's near-white ground, same hairline-on-#fff
   treatment as .chart-block — a dark terminal block fights everything
   else on the page. */
.bibtex-code {
  background-color: #fff;
  color: var(--ink-soft);
  padding: 20px 22px;
  margin: 60px auto 20px;
  max-width: 100%;
  box-sizing: border-box;
  font-size: 13px;
  border: 1px solid var(--rule);
}

.bibtex-title {
  font-family: 'JetBrains Mono', monospace;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 12px;
  color: var(--ink);
}

.bibtex-code pre {
  white-space: pre-wrap;
  overflow-wrap: break-word;
  margin: 0;
  min-height: 4.6em;               /* placeholder: holds the frame open while
                                      the entry is still empty */
}

.bibtex-code code {
  font-family: 'JetBrains Mono', 'Courier New', monospace;
  overflow-wrap: break-word;
  font-size: 12.5px;
  line-height: 1.55;
}

/* =================================================================
   FOOTER
   One line — the compression note — over a hairline rule, with a deep
   pad under it so the page ends in whitespace instead of stopping dead
   at the last glyph.
   ================================================================= */
.footer {
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  color: var(--ink-faint);
  margin-top: 60px;
  border-top: 1px solid var(--rule-faint);
  padding: 24px 0 110px;
  text-align: center;
}
.footer a {
  color: var(--ink-soft);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-faint);
}
.footer a:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* =================================================================
   RESPONSIVE
   ================================================================= */
@media (max-width: 720px) {
  body { padding: 30px 16px 20px; }
  .hero-film { margin: -30px -16px 36px; --hero-shrink: 0.05; }
  .hero-film-wordmark { left: 20px; bottom: 20px; }
  .hero-film-scroll { right: 20px; bottom: 26px; font-size: 12px; }
  .hero-text { font-size: 38px; }
  .sub-hero-text { font-size: 17px; margin-bottom: 24px; }

  .r2s-video-row { flex-direction: column; }
  .r2s-caption-row { flex-direction: column; gap: 4px; }

  .additional-video-row { flex-direction: column; }
  .additional-video-item { height: 180px; }

  .rollout-grid { grid-template-columns: 1fr; }
  .rollout-cell video { height: 200px; }

  .gallery-video { height: 160px; }

  .quick-links { gap: 8px; }
  .quick-links a { font-size: 12px; padding: 9px 13px; }

  .chart-block { padding: 14px 8px; }

  /* The frame gets short on phones (2.35:1 of a narrow column), so pull the
     button in and shrink it rather than letting it crowd the robot. */
  .live-demo-cta {
    left: 12px;
    bottom: 12px;
    max-width: calc(100% - 24px);
    font-size: 14px;
    padding: 11px 18px;
  }
}
