/* ============================================================================
   UniMate — project page styles.

   Built on the VideoMimic template, then trimmed to only the rules this page
   uses. Palette tokens live in :root; fonts are Playfair Display (headings) and
   Roboto Mono (labels/mono), over a Georgia serif body.

   Sections:
     1. Design tokens
     2. Base (body, focus, selection)
     3. Table of contents (fixed left rail)
     4. Layout + hero (title, authors, affiliations, quick links)
     5. Teaser video + figure captions
     6. Section headings
     7. Video galleries
     8. Interactive 3D viewer (sidebar + canvas)
     9. BibTeX
    10. Footer
    11. Responsive
   ============================================================================ */

@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=Roboto+Mono:wght@300;700&display=swap');

/* ── 1. Design tokens ───────────────────────────────────────────────────────── */
:root {
  --ivory: #faf9f5;
  --ivory-medium: #f0eee6;
  --ivory-dark: #e8e6dc;
  --slate-dark: #141413;
  --slate-light: #5e5d59;
  --accent: #d97757;
  /* Quiet grey for quick links that aren't live yet — shared by the dead
     placeholders and the "coming soon" ones, so they stay in sync. */
  --link-muted: #b8b4a8;
  /* Hairline for structural rules. Tinted ink rather than --ivory-dark, which
     is a *surface* tone: at 1px on cream it disappears, and the rule beside a
     solid ink plate has to register as a line. */
  --rule: rgba(20, 20, 19, 0.18);
}

/* ── 2. Base ────────────────────────────────────────────────────────────────── */
body {
  background-color: #fdfaf4;
  font-family: Georgia, 'Times New Roman', serif;
  color: #1b1b1b;
  padding: 60px 30px;
}

html { scroll-behavior: smooth; }

/* On-brand text selection. */
::selection {
  background: rgba(217, 119, 87, 0.28); /* --accent */
  color: var(--slate-dark);
}

/* Visible keyboard focus (quality floor), in the accent. */
a:focus-visible,
button:focus-visible,
.example-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ── 3. Table of contents (fixed left rail, shown ≥1400px) ──────────────────── */
/* The list is a progress rail: a hairline track whose segment lights up coral
   beside the section you're currently reading (see the scroll-spy script). */
.toc {
  width: 192px;
  font-family: 'Roboto Mono', monospace;
  position: fixed;
  top: 50%;                     /* vertically centered in the viewport */
  transform: translateY(-50%);
  max-height: 90vh;
  overflow-y: auto;
  display: none;                /* revealed by the media query below */
  z-index: 1000;
  transition: opacity 0.2s ease;
}

@media (min-width: 1400px) {
  .toc {
    display: block;
    left: max(30px, calc(25vw - 330px));
  }
}

/* Set while a full-bleed gallery is running under the rail; see the collision
   watcher in index.html for when that happens. Opacity rather than display or
   visibility, so the element keeps its box and the watcher can go on measuring
   it while hidden. */
.toc.is-eclipsed {
  opacity: 0;
  pointer-events: none;
}

/* Deliberately not a heading element: this labels a navigation rail, it is not
   a section of the document. As an <h3> it was the page's first heading and
   sat above every <h2>, which inverts the outline a screen reader reads out.
   font-weight is therefore explicit — it used to come free from the h3 UA
   default, and a plain <div> would drop to regular. */
.toc-title {
  font-family: 'Playfair Display', serif;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.4px;
  margin-top: 0;
  margin-bottom: 8px;
  color: var(--slate-dark);
}

.toc hr {
  border: 0;
  height: 1px;
  background-color: var(--ivory-dark);
  margin-bottom: 16px;
}

.toc ul {
  list-style: none;
  padding: 0;
  margin: 0;
  position: relative;
  border-left: 1.5px solid var(--ivory-dark);
}

.toc li {
  position: relative;
  margin-bottom: 7px;
}

/* Give each top-level section a little air above it, so its sub-items below
   read as a group (the first item needs none). */
.toc li:not(.toc-subsection) {
  margin-top: 14px;
}
.toc li:first-child {
  margin-top: 0;
}

.toc a {
  display: block;
  position: relative;
  text-decoration: none;
  color: var(--slate-light);
  font-size: 14px;
  line-height: 1.4;
  padding: 2px 0 2px 16px;
  transition: color 0.18s ease;
}

/* The playhead segment — fills the rail next to the active item. */
.toc a::before {
  content: '';
  position: absolute;
  left: -1.5px;
  top: 3px;
  bottom: 3px;
  width: 2px;
  background: var(--accent);
  border-radius: 1px;
  transform: scaleY(0);
  transform-origin: top center;
  transition: transform 0.2s ease;
}

.toc a:hover {
  color: var(--slate-dark);
}

.toc a.active {
  color: var(--accent);
  font-weight: 700;
}

.toc a.active::before {
  transform: scaleY(1);
}

/* Subsections stay flush to the shared rail; the text is indented instead, so
   every progress marker lines up on one track. */
.toc-subsection {
  margin-bottom: 5px;
}
.toc-subsection a {
  font-size: 12px;
  padding-left: 30px;
  color: #8a887f;
}
.toc-subsection a.active {
  color: var(--accent);
}
.toc-subsection a:hover {
  color: var(--slate-light);
}

/* ── 4. Layout + hero ───────────────────────────────────────────────────────── */
.main-content {
  max-width: 960px;
  margin: 0 auto;
}

/* The page's <h1>. margin-top is pinned because h1's UA default is 0.67em —
   at this size that is a 40px drop nobody asked for. */
.hero-text {
  font-size: 60px;
  font-weight: 700;
  letter-spacing: -1px;
  line-height: 1.1;
  margin-top: 0;
  margin-bottom: 10px;
}

.sub-hero-text {
  font-size: 40px;
  font-weight: 400;
  line-height: 1.2;
  margin-bottom: 50px;
  white-space: nowrap;
}

.authors {
  font-family: 'Roboto Mono', monospace;
  font-size: 16px;
  color: #333;
  line-height: 1.6;
  margin-top: -32.5px;   /* pull up under the subtitle */
  margin-bottom: 17.5px;
  text-align: left;
  white-space: nowrap;
}
.authors a {
  color: inherit;
  text-decoration: none;
  text-underline-offset: 2px;
  transition: color 0.18s ease;
}
.authors a:hover,
.authors a:focus-visible {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--accent);
}

/* Affiliation logos — no figure border. */
.affiliations {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 56px;
  margin: 24px 0 16px;
}
.affiliations img {
  width: auto;
  height: 84px;
  border: 0;
  border-radius: 0;
  margin: 0;
}
.affiliations img.logo-lg { height: 112px; }

/* Venue — the page's one credential, set as an eyebrow above the title.
   It used to be a filled ink box, deliberately the same device as .tagline and
   .section-subtitle. With eleven of those boxes now on the page, a twelfth at
   14px stopped reading as authority and started reading as a section heading
   that had shrunk — and it sat directly above a 60px title, so the two competed.
   The box is gone; the mark carries the identity instead: a root joint, a bone,
   a child joint. That is the smallest kinematic chain, the primitive this whole
   model generalises over, and it is the one element at the top of the page drawn
   from the paper's own subject.
   Still deliberately *not* accent-coloured: coral is reserved on this page for
   interactive state (link hover, focus, active TOC, selection), so a static
   line must not claim it. Flush left so it shares the hero's alignment spine
   with .hero-text / .sub-hero-text / .authors — centered under the logo cluster
   it just floated between two blocks with nothing to anchor to. */
.venue-line {
  display: flex;
  align-items: center;
  gap: 11px;
  /* A hairline mark has less optical mass than the old filled box, so it needs
     a little more air below to hold the title off it. */
  margin-bottom: 18px;
}

/* Root solid, child hollow — the distinction rig UIs draw, and at this size the
   thing that makes the mark read as two nodes joined by a bone rather than as a
   bullet followed by a dash. The bone stops at the child's stroke (x2=27, not
   29) so nothing shows through the open joint; the joint is fill:none rather
   than a paper colour, so it cannot desync from the background. */
.venue-rig {
  flex: none;
  width: 34px;
  height: 8px;
}
.venue-rig line {
  stroke: var(--slate-dark);
  stroke-width: 1;
}
.venue-rig-root {
  fill: var(--slate-dark);
}
.venue-rig-joint {
  fill: none;
  stroke: var(--slate-dark);
  stroke-width: 1;
}

.venue {
  font-family: 'Roboto Mono', monospace;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  /* Out of the box the label has to hold itself together as a line of record,
     which takes more tracking than it did as a solid block (was 0.08em). */
  letter-spacing: 0.14em;
  line-height: 1;
  color: var(--slate-dark);
}

/* The venue name is the credential; the year only dates it. Splitting weight
   and ink says so without adding a separator. */
.venue-year {
  font-weight: 300;
  color: var(--slate-light);
}

/* Quick links. Live links get a sliding coral underline; disabled ones read dead. */
.quick-links {
  text-align: center;
  white-space: nowrap;   /* keep all links on one line */
  margin-top: 20px;
  margin-bottom: 30px;
}
.quick-links a {
  position: relative;
  font-family: 'Roboto Mono', monospace;
  font-size: 18px;
  font-weight: bold;
  color: #333;
  text-decoration: none;
  margin: 0 8px;
  padding: 5px 8px;
  transition: color 0.18s ease;
}
.quick-links a:not(.disabled)::after {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.22s ease;
}
/* :focus-visible, not :focus — a mouse click leaves focus on the anchor, so with
   plain :focus the coral + underline stayed lit after returning from the opened
   tab. Keyboard users still get the state (and the global focus ring above). */
.quick-links a:not(.disabled):hover,
.quick-links a:not(.disabled):focus-visible {
  color: var(--accent);
  text-decoration: none;
}
.quick-links a:not(.disabled):hover::after,
.quick-links a:not(.disabled):focus-visible::after {
  transform: scaleX(1);
}
.quick-links a.disabled {
  color: var(--link-muted);
  cursor: not-allowed;
  text-decoration: none;
  pointer-events: none;
}

/* "Coming soon" sits at the same grey as the dead placeholders, but stays live:
   it is not .disabled, so the coral hover and sliding underline above still fire
   and the link keeps its affordance despite the quiet resting colour. */
.quick-links a.pending {
  color: var(--link-muted);
}

/* ── 5. Teaser video + figure captions ──────────────────────────────────────── */
/* Teaser video framed like a figure. */
#teaser-video {
  width: 100%;
  border: 1.5px solid #000;
  border-radius: 15px;
  box-sizing: border-box;
  display: block;
}

.figure-caption {
  font-family: 'Roboto Mono', monospace;
  font-size: 14px;
  color: var(--slate-light);
  text-align: center;
  margin-top: 10px;
  margin-bottom: 40px;
  padding: 0 20px;
}

/* ── 6. Section headings ────────────────────────────────────────────────────── */
/* Warm ink boxes: near-black (not pure #000) carries a hint of warmth on cream.
   Both ranks use that box; what separates them is the hairline the section rank
   trails across the column. Size alone did not — at 24px vs 20px "Experiments."
   and "1. Diverse Skeletons, Diverse Prompts." scanned as the same object.
   scroll-margin keeps a heading clear of the top edge on TOC anchor jumps. */

/* Rank 1 (h2). The flex row is the heading; the plate is the <span> inside it.
   Splitting them is what lets ::after trail a hairline from the plate to the
   edge of the text column — the device that makes this read as a chapter
   division rather than a sticker floating on the page. */
.tagline {
  display: flex;
  align-items: center;
  gap: 16px;
  font-family: 'Roboto Mono', monospace;
  font-size: 24px;
  /* Explicit, because h2's UA default is bold and the @import ships only 300
     and 700 — an unset weight would snap the plate to a much heavier face. */
  font-weight: 300;
  /* More room above (separate from prior section) than below (hug the content
     this heading introduces). Being block-level, the top margin COLLAPSES with
     the previous element's bottom margin rather than adding to it, and the
     tallest of those is .figure-caption's 40px. So any value past 40 sets every
     section break to exactly itself — 56px for a uniform rhythm. Below 40 the
     gaps go ragged, each pinned by whatever happens to precede it. */
  margin: 56px 0 18px;
  scroll-margin-top: 32px;
}
.tagline > span {
  background: var(--slate-dark);
  color: #fff;
  /* Mono set solid on ink needs air between letters to read as one block —
     the same reasoning as .venue, which is this device one size down. */
  letter-spacing: 0.04em;
  /* Right padding is short by the tracking amount: letter-spacing appends a
     trailing gap after the final glyph, so equal padding reads off-center. */
  padding: 8px 16px 8px 17px;
}
.tagline::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--rule);
}

/* Rank 2: the same plate one size down, and deliberately still inline-block —
   an unfilled variant (ink bar in the left margin) was tried and reverted,
   since the section rule above already tells the ranks apart. Every instance
   is styled identically on purpose; see the comment above
   #diverse-skeletons-prompts in index.html. */
.section-subtitle {
  font-family: 'Roboto Mono', monospace;
  font-size: 20px;
  background: var(--slate-dark);
  color: #fff;
  display: inline-block;
  padding: 6px 14px;
  margin: 26px 0 14px;
  scroll-margin-top: 32px;
}

.section {
  font-size: 20px;
  line-height: 1.7;
  margin-bottom: 24px;
}

/* ── 7. Video galleries ─────────────────────────────────────────────────────── */
/* Full-bleed section so the scroll strip can run wider than the text column. */
.video-gallery-section {
  margin-bottom: 22px;
  width: 100vw;
  position: relative;
  margin-left: calc((100% - 100vw) / 2);
  box-sizing: border-box;
}

/* Scrollable track (scrollbar hidden). */
.video-gallery-container {
  box-sizing: border-box;
  max-width: 1150px;
  margin-left: auto;
  margin-right: auto;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;      /* Firefox */
  -ms-overflow-style: none;   /* IE and Edge */
}
.video-gallery-container::-webkit-scrollbar {
  display: none;              /* Chrome, Safari, Opera */
}

/* The flex row holding the videos. */
.video-gallery {
  display: flex;
  gap: 15px;
  padding: 0 20px 10px;
  box-sizing: border-box;
  width: max-content;
  margin: 0 auto;
}

.gallery-video {
  flex: 0 0 auto;
  height: 220px;
  width: auto;                /* keep each clip's aspect ratio */
  border-radius: 15px;
  box-sizing: border-box;
  background-color: #fdfaf4;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

/* Experiments galleries: taller videos with a bordered frame. */
#transferGallerySection .gallery-video,
#promptGallerySection .gallery-video,
#motionGallerySection .gallery-video,
#demoGallerySection .gallery-video,
#editGallerySection .gallery-video,
#inbetweenGallerySection .gallery-video,
#expansionGallerySection .gallery-video {
  height: 300px;
  border: 1.5px solid #000;
}

/* Applications show a single clip each — make it large so it fills the column. */
#editGallerySection .gallery-video,
#inbetweenGallerySection .gallery-video,
#expansionGallerySection .gallery-video {
  height: 500px;
}

/* The overview clips sit between the two sizes: taller than an experiment strip
   because each is a 2x3 grid whose prompt text is baked into the frame and needs
   the pixels, but not application-size — at 500px a single clip ran 875px wide
   and read as the biggest thing in Experiments, outweighing the studies it is
   only meant to introduce. 400px (~698px wide) is the smallest size that keeps
   the baked-in prompts legible. Don't go lower without checking that text. */
#demoGallerySection .gallery-video {
  height: 400px;
}

/* Horizontal scroll buttons. */
.gallery-nav {
  background-color: rgba(40, 40, 40, 0.8);
  color: white;
  border: none;
  border-radius: 50%;
  width: 35px;
  height: 35px;
  font-size: 20px;
  font-weight: bold;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.2s ease;
  margin: 0 5px;
  flex-shrink: 0;
}
.gallery-nav:hover {
  background-color: rgba(0, 0, 0, 1);
}
.gallery-nav-controls {
  display: flex;
  align-items: center;
  margin-right: 15px;         /* space between buttons and caption text */
}

/* Caption + nav-button row, centered on the content column. */
.gallery-caption-container {
  max-width: 960px;
  margin: 15px auto 0 auto;
  padding: 0 20px;
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
.figure-caption.gallery-caption {
  margin-top: 0;
  margin-bottom: 0;
  text-align: left;
  flex-grow: 1;
}

/* ── 8. Interactive 3D viewer (sidebar + canvas) ────────────────────────────── */
.viewer-layout {
  display: flex;
  gap: 14px;
  align-items: stretch;
}

/* The sidebar reads as a kinematic chain: a bone runs down the left gutter and
   each item is a joint on it. Selecting a character activates its joint. */
.example-sidebar {
  position: relative;
  flex: 0 0 165px;
  height: 590px;
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding-left: 22px;
}

/* The bone (spine) — inset so it begins/ends at the first/last joint centers. */
.example-sidebar::before {
  content: '';
  position: absolute;
  left: 7px;
  top: 28px;
  bottom: 28px;
  width: 2px;
  background: var(--ivory-dark);
  border-radius: 1px;
}

.example-item {
  position: relative;
  flex: 1 1 0;
  font-family: 'Roboto Mono', monospace;
  font-size: 14px;
  font-weight: 700;
  text-align: left;
  white-space: nowrap;
  padding: 10px 11px;
  border: 1px solid var(--ivory-dark);
  border-radius: 8px;
  background: #fff;
  color: var(--slate-dark);
  cursor: pointer;
  transition: border-color 0.18s ease, background 0.18s ease;
}

/* The joint — a hollow node sitting on the spine. */
.example-item::before {
  content: '';
  position: absolute;
  box-sizing: border-box;
  left: -20px;
  top: 50%;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--ivory);
  border: 2px solid var(--ivory-dark);
  transition: background 0.2s ease, border-color 0.2s ease,
              width 0.2s ease, height 0.2s ease, left 0.2s ease, margin-top 0.2s ease;
}

/* The bone that wires the joint to its rig — grows out on activation. */
.example-item::after {
  content: '';
  position: absolute;
  left: -8px;
  top: 50%;
  width: 8px;
  height: 2px;
  margin-top: -1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.25s ease;
}

.example-item:hover {
  border-color: var(--slate-light);
}
.example-item:hover::before {
  border-color: var(--slate-light);
}

.example-item.active {
  border-color: var(--slate-light);
  background: var(--ivory-medium);
}

/* Activated joint: filled coral, slightly larger. */
.example-item.active::before {
  width: 14px;
  height: 14px;
  left: -21px;
  margin-top: -7px;
  background: var(--accent);
  border-color: var(--accent);
}
.example-item.active::after {
  transform: scaleX(1);
}

.viewer-wrapper {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  height: 620px;
  border: 1.5px solid #000;
  border-radius: 15px;
  overflow: hidden;
  background: var(--ivory-medium);
}
.viewer-wrapper canvas {
  display: block;
}

/* lil-gui toolbar, pinned inside the viewer. */
.viewer-wrapper .lil-gui {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 3;
  --background-color: rgba(250, 249, 245, 0.94);
  --title-background-color: rgba(240, 238, 230, 0.96);
  --text-color: #141413;
  --title-text-color: #141413;
  --widget-color: #e8e6dc;
  --focus-color: #d97757;
  --width: 160px;
  --name-width: 52%;
  --widget-height: 16px;
  --padding: 4px;
  --spacing: 4px;
  font-size: 10.4px;
  border-radius: 7px;
  overflow: hidden;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
}

#loading-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(250, 249, 245, 0.85);
  color: var(--slate-light);
  font-weight: 600;
  letter-spacing: 0.02em;
  z-index: 2;
}

.spinner {
  width: 22px;
  height: 22px;
  margin-right: 10px;
  border: 3px solid var(--ivory-dark);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

.viewer-hint {
  font-family: 'Roboto Mono', monospace;
  text-align: center;
  color: var(--slate-light);
  font-size: 14px;
  margin-top: 14px;
}

#drop-hint {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1.2em;
  color: var(--accent);
  background: rgba(253, 246, 238, 0.92);
  border: 3px dashed var(--accent);
  border-radius: 15px;
  z-index: 4;
  pointer-events: none;
}

/* ── 9. BibTeX ──────────────────────────────────────────────────────────────── */
.bibtex-code {
  background-color: var(--slate-dark);   /* warm ink — matches the heading boxes */
  color: var(--ivory-dark);
  padding: 22px 24px;
  border-radius: 10px;
  margin: 52px auto -40px;
  max-width: 1000px;
  box-sizing: border-box;
  font-size: 14px;
}
.bibtex-title {
  font-family: 'Roboto Mono', monospace;
  font-weight: 700;
  font-size: 1.05em;
  letter-spacing: 0.06em;
  margin-bottom: 12px;
  color: #fff;
}
.bibtex-code pre {
  white-space: pre-wrap;
  overflow-wrap: break-word;
  margin: 0;
}
.bibtex-code code {
  font-family: 'Roboto Mono', monospace;
  font-weight: 300;
  overflow-wrap: break-word;
}

/* ── 10. Footer ─────────────────────────────────────────────────────────────── */
.footer {
  font-size: 14px;
  font-family: 'Roboto Mono', monospace;
  color: var(--slate-light);
  margin-top: 80px;
  border-top: 1px solid var(--ivory-dark);
  padding-top: 30px;
  text-align: center;
}
.footer-related {
  margin: 0 0 12px;
  color: var(--slate-light);
}
.footer a {
  color: var(--slate-dark);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.18s ease;
}
.footer a:hover {
  color: var(--accent);
}

/* ── 11. Responsive ─────────────────────────────────────────────────────────── */
@media (max-width: 1000px) {
  .sub-hero-text {
    font-size: clamp(28px, 5vw, 40px);
    white-space: normal;
  }
  .authors {
    white-space: normal;
  }
  .quick-links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    white-space: normal;
  }

  /* The two tallest galleries outrun the viewport well before the phone
     breakpoint below: a §1 overview clip is 698px wide and an Applications clip
     873px, so on a tablet in portrait they are already clipped. min() shrinks
     them to fit and can never enlarge them, so every width above the crossover
     keeps the tuned size exactly. The fractional px values are the widths those
     tuned heights already render at, to the sub-pixel: 400 and 500 are
     border-box, so it is (height - 3px of border) x 1.75 + 3, not height x 1.75.
     Rounding them to whole pixels shifts the page a pixel at widths where
     nothing should move at all, which is the one thing this rule must not do.
     Recompute both if either height changes.
     The experiment strips (523px) fit this whole band and are left alone. */
  #demoGallerySection .gallery-video {
    width: min(697.75px, calc(100vw - 40px));
    height: auto;
  }
  #editGallerySection .gallery-video,
  #inbetweenGallerySection .gallery-video,
  #expansionGallerySection .gallery-video {
    width: min(872.75px, calc(100vw - 40px));
    height: auto;
  }
}

@media (max-width: 720px) {
  /* 20px, not the desktop 30px, for two reasons: 30px of gutter on a 390px
     screen spends 15% of the width on nothing, and — the reason it is exactly
     20 — the full-bleed galleries below resolve to `100vw - 40px`, so at a 20px
     gutter the clips land flush with the text column instead of near it. This
     breakpoint and that width are one decision; keep them in step.
     `margin: 0` is what makes that true. The file never resets the UA's 8px
     body margin, so the real gutter is padding + 8 — which is invisible on
     desktop but here left the text column 8px inside the clips, the two
     measures missing each other by just enough to look like a mistake. Reset
     on phones only; the desktop 30px + 8px is left exactly as it was. */
  body { margin: 0; padding: 40px 20px; }

  .viewer-layout { flex-direction: column; }
  .example-sidebar {
    flex: none;
    height: auto;
    align-self: stretch;
    flex-direction: row;
    flex-wrap: nowrap;
    gap: 8px;
    padding: 0 0 6px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
  }
  .example-sidebar::before,
  .example-item::before,
  .example-item::after { display: none; }
  .example-item {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
  .example-item.active { border-color: var(--accent); background: var(--ivory-medium); }
  .viewer-wrapper { height: 420px; }

  /* Every gallery, not just Applications. The clip sizes above are all fixed
     HEIGHTS, so on a phone width follows the 1.75:1 aspect and runs off-screen —
     698px for a §1 overview clip, 523px for an experiment strip, against a text
     column of 350px. Nothing on the page then shares a measure, which is what
     reads as "unscaled" on mobile: the videos are simply not in the same layout
     as the prose. Driving width instead (and letting height follow) puts every
     clip flush with the text column — see the body padding at the top of this
     block, set to 20px precisely so `100vw - 40px` lands on the same edges.
     The cost is real and deliberate: at 350px wide the prompt text baked into
     the §1 grids is too small to read, which is exactly what the 400px height
     was tuned to preserve. It cannot be preserved here — a 2x3 grid of labelled
     panels does not fit a phone at any legible scale — and a clip you can only
     see half of doesn't preserve it either. Desktop keeps the tuned sizes.
     Spelled out per section id rather than as a bare `.gallery-video`: a media
     query adds no specificity, so one class would lose to the `#xGallerySection
     .gallery-video` heights above. Add a gallery here when you add one there. */
  #demoGallerySection .gallery-video,
  #transferGallerySection .gallery-video,
  #promptGallerySection .gallery-video,
  #motionGallerySection .gallery-video,
  #editGallerySection .gallery-video,
  #inbetweenGallerySection .gallery-video,
  #expansionGallerySection .gallery-video {
    width: calc(100vw - 40px);
    height: auto;
  }
}

/* Phones. Everything below is a translation of a desktop device to a ~350px
   column, not a different design: the ink plates, the trailing hairline, the
   left-aligned hero spine and the centred logo/link cluster all survive — they
   are just re-proportioned for a 350px measure, a third of the 960px column
   they were set for. Nothing here applies above 600px; desktop is untouched. */
@media (max-width: 600px) {
  /* 60px is a desktop display size; at this width it is a third of the screen
     and crowds the subtitle under it. */
  .hero-text { font-size: 44px; }
  .sub-hero-text { font-size: 22px; margin-bottom: 30px; white-space: normal; }
  .authors { white-space: normal; }

  /* The three logos measure 267px side by side, so at the desktop 56px gap —
     or the 36px this used to carry — they wrapped 2 + 1 and the cluster stood
     176px tall, a third of the first screen spent on wordmarks. 22px is the
     widest gap that still keeps them on one row, so they read as one line of
     institutions rather than a stack. */
  .affiliations { gap: 22px; margin: 20px 0 14px; }
  .affiliations img { height: 60px; }
  .affiliations img.logo-lg { height: 80px; }

  /* The four links ran to three lines — [arXiv] alone, then [Code (coming
     soon)] alone — which read as a list of four separate things rather than one
     row of links. 18px overshoots the column by 6px; 16px fits two per line. */
  .quick-links a {
    font-size: 16px;
    margin: 0 5px;
  }

  /* Headings. The plate is a label, not a banner: at 24px/20px these wrapped
     and became full-width slabs of solid ink — 334x65 for a rank-2 heading on a
     350px column, the heaviest object on the page and heavier than the videos
     they introduce. Shrinking the type restores the intended reading at this
     width, and for rank 1 it restores the device outright: the hairline can
     only trail from the plate to the column edge if the plate leaves room for
     it, so a smaller plate makes that section rule MORE visible here, not less.
     Ranks stay 3px apart and keep the rule/no-rule distinction. */
  .tagline {
    font-size: 17px;
    gap: 12px;
    /* Same logic as the desktop 56px: it must clear .figure-caption's 40px
       bottom margin, which does not shrink here, or the collapse leaves the
       section breaks ragged. 44 is the smaller value that still wins. */
    margin: 44px 0 14px;
  }
  .tagline > span { padding: 6px 12px 6px 13px; }
  .section-subtitle {
    font-size: 14px;
    padding: 6px 12px;
    margin: 24px 0 12px;
  }

  /* Body copy. 20px on a 350px column runs ~33 characters to the line; 18px
     buys back four or five and reads less like large print. */
  .section { font-size: 18px; line-height: 1.65; }

  /* Captions lose their 20px inset. On desktop it holds the caption inside the
     wider figure; here the figure IS the column, so the inset only pushed the
     caption out of alignment with the video above it. */
  .figure-caption { padding: 0; }

  /* Caption row stacks. Side by side, the buttons took 90px of a 350px row and
     left the caption a 245px ragged strip set against a full-width video —
     the one place on the phone where two different measures sat touching. */
  .gallery-caption-container {
    flex-direction: column;
    /* stretch, not the row layout's flex-start: once this is a column, the
       cross axis is horizontal, and flex-start would size the caption to its
       own text. Long captions clamp to the column and look right by accident;
       a short one would sit in a narrow box under a full-width video. */
    align-items: stretch;
  }
  .gallery-nav-controls {
    margin-right: 0;
    margin-bottom: 12px;
  }
  /* 35px is under the 44px minimum touch target, and these are the only
     controls on the page a thumb has to hit. */
  .gallery-nav {
    width: 44px;
    height: 44px;
    margin: 0 10px 0 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .example-item,
  .example-item::before,
  .example-item::after { transition: none; }
  .quick-links a:not(.disabled)::after { transition: none; }
}
