/* =====================================================
   css/styles.css — project page styles
   Academic direction, light theme
   =====================================================
   All colors are tokens on :root. Reach for a token,
   never a literal, when adding UI.
   ===================================================== */

:root {
  color-scheme: light;

  /* Surfaces */
  --bg: #F1F3F6;
  --panel: #FFFFFF;
  --panel-2: #F1F3F6;
  --panel-3: #F7F8FA;

  /* Ink */
  --ink: #171B24;
  --ink-soft: #5C6577;
  --ink-dim: #6F7787;

  /* Structure */
  --rule: #D9DEE6;
  --rule-strong: #171B24;

  /* Accents */
  --accent: #0F6E93;   /* behavioral variations, links, primary UI */
  --warm: #A75A14;     /* visual + behavioral variations, placeholders */
  --spec-label: var(--accent); /* labels beside the task specs on task pages */
  --contact: #BF3A2B;  /* "perceive or react to contact events" and the challenges it covers */
  --tactile: #BF3A2B;  /* the numbers on the visual-tactile findings (cards and clip titles) */

  /* Policies in the task progress tables (the paper's Fig. 9): a hue per
     base policy, a lighter step of it for its visual-tactile variant */
  --m-dp: #2A78D6;
  --m-rdp: #8CB6EA;
  --m-pi05: #EB6834;
  --m-ftp1: #F5AE92;

  /* Compound tokens */
  --chip-bg: rgba(15, 110, 147, 0.07);
  --b-wash: rgba(15, 110, 147, 0.07);    /* behavioral variation headers */
  --vb-wash: rgba(167, 90, 20, 0.08);    /* visual + behavioral variation headers */
  --b-label-bg: rgba(15, 110, 147, 0.13);   /* labels on behavioral variation stills */
  --vb-label-bg: rgba(167, 90, 20, 0.14);   /* labels on visual + behavioral variation stills */
  --ph-bg: rgba(167, 90, 20, 0.09);
  --contact-wash: rgba(191, 58, 43, 0.08);
  --tactile-wash: rgba(191, 58, 43, 0.09);
  --zone-shadow: rgba(13, 16, 22, 0.22);  /* under a part of the teaser that the pointer lifts */
  --video-bg: #E5E9EE;
  --play-overlay: rgba(0, 0, 0, 0.04);
  --vidmeta-bg: rgba(255, 255, 255, 0.9);
  --tag-bg: rgba(255, 255, 255, 0.72);     /* the tags over the visual-tactile clips' captions */
  --backdrop: rgba(13, 16, 22, 0.86);     /* behind the video lightbox */
  --on-dark: #FFFFFF;                      /* text on the lightbox backdrop */
  --on-dark-soft: rgba(255, 255, 255, 0.72);
  --scrim: rgba(13, 16, 22, 0.32);         /* behind captions laid over video stills */
  --scrim-shadow: rgba(13, 16, 22, 0.8);   /* the halo around those captions' letters */
  --on-dark-track: rgba(255, 255, 255, 0.38);  /* unfilled ticks over video */
  --tile-scrim:                            /* skill tiles: under the ticks and the name */
    linear-gradient(to top, rgba(13, 16, 22, 0.8), rgba(13, 16, 22, 0.42) 32%, rgba(13, 16, 22, 0) 58%),
    linear-gradient(to bottom, rgba(13, 16, 22, 0.34), rgba(13, 16, 22, 0) 24%);
  --preview-pattern:
    linear-gradient(135deg, rgba(0, 0, 0, 0.03), rgba(0, 0, 0, 0.08)),
    repeating-linear-gradient(45deg, transparent 0 7px, rgba(0, 0, 0, 0.04) 7px 8px);

  /* Shape and type */
  --radius: 6px;
  --sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

  /* Set by main.js: height of the sticky contents strip on narrow screens */
  --strip-h: 0px;
}

/* ---------- Base ---------- */

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
[hidden] { display: none !important; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, svg, video { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
details summary { list-style: none; cursor: pointer; user-select: none; }
details summary::-webkit-details-marker { display: none; }
details summary::marker { display: none; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mono { font-family: var(--mono); }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Paragraphs from the configs */
.prose p { margin: 0; }
.prose p + p { margin-top: 8px; }

/* Text still to be filled in */
.ph {
  background: var(--ph-bg);
  border-bottom: 1px dashed var(--warm);
  padding: 0 3px;
}

/* ---------- Page shell ---------- */

.page { padding-block: 56px 120px; padding-inline: 24px; }
.page-inner { margin: 0 auto; }

/* ---------- Cover: title block + teaser ---------- */

.cover {
  background: var(--panel);
  border-bottom: 1px solid var(--rule);
  padding-block: 72px 48px;
  padding-inline: 24px;
}
.cover-inner { max-width: 1240px; margin: 0 auto; text-align: center; }
.cover h1 {
  font-family: var(--sans); font-weight: 700;
  font-size: clamp(28px, 4vw, 42px); line-height: 1.22; letter-spacing: -0.02em;
  margin: 0 0 22px; color: var(--ink); text-wrap: balance;
}
/* The benchmark's name on its own line, the rest of the title under it */
.cover h1 .title-name { display: block; font-size: 1.4em; letter-spacing: -0.03em; margin-bottom: 6px; }
.cover h1 .title-sub { display: block; font-size: 0.8em; font-weight: 400; letter-spacing: -0.01em; color: var(--ink-soft); }
.authors {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 22px;
  font-size: 18px; line-height: 1.5; margin: 0 0 6px; color: var(--ink);
}
.authors a { color: var(--accent); text-decoration: none; }
.authors a:hover { text-decoration: underline; }
.authors sup { font-size: 0.68em; margin-left: 1px; }
.affiliations {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 22px;
  font-size: 15px; line-height: 1.5; margin: 0 0 26px; color: var(--ink-soft);
}
.affiliations sup { font-size: 0.68em; margin-right: 2px; }
.links { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.link-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 18px 8px 15px; text-decoration: none; border-radius: 999px;
  background: var(--ink); color: var(--panel);
  font-size: 15px; font-weight: 500; line-height: 1.3;
  transition: background .15s;
}
.link-btn .ico {
  width: 15px; height: 15px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.link-btn:hover { background: var(--accent); }
/* Link not set yet */
.link-btn[data-placeholder] { background: var(--ink-soft); }
.link-btn[data-placeholder]:hover { background: var(--ink-dim); }
/* "Coming soon" over a link not set yet, on hover or keyboard focus */
.link-btn[data-placeholder] { position: relative; }
.link-tip {
  position: absolute; bottom: calc(100% + 7px); left: 50%; transform: translate(-50%, 3px);
  padding: 3px 9px; border-radius: var(--radius); white-space: nowrap;
  background: var(--ink); color: var(--panel); font-size: 13px; font-weight: 500;
  opacity: 0; pointer-events: none; transition: opacity .15s, transform .15s;
}
.link-btn:hover .link-tip, .link-btn:focus-visible .link-tip { opacity: 1; transform: translate(-50%, 0); }

.teaser { position: relative; max-width: 1240px; margin: 44px auto 0; }
.teaser-img { display: block; width: 100%; height: auto; }
/* Links over the teaser's three parts (index.html gives each its box, in
   fractions of the image). On hover a part is outlined and grows a little:
   each holds a copy of the image (.teaser-zoom, put there by js/main.js),
   placed so only its part shows, and shown only then. */
.teaser-zone {
  position: absolute; overflow: hidden; border-radius: 4px;
  left: calc(var(--x) * 100%); top: calc(var(--y) * 100%);
  width: calc(var(--w) * 100%); height: calc(var(--h) * 100%);
  transition: transform .2s ease, box-shadow .2s ease;
}
.teaser-zoom {
  position: absolute; max-width: none;
  left: calc(var(--x) / var(--w) * -100%); top: calc(var(--y) / var(--h) * -100%);
  width: calc(100% / var(--w)); height: calc(100% / var(--h));
  opacity: 0; transition: opacity 0s .2s;   /* hidden once the part has shrunk back */
}
.teaser-zone:hover { z-index: 1; box-shadow: 0 0 0 2px var(--accent), 0 8px 24px var(--zone-shadow); }
.teaser-zone:hover .teaser-zoom { opacity: 1; transition: none; }
@media (prefers-reduced-motion: no-preference) {
  .teaser-zone:hover { transform: scale(1.02); }
}
.teaser-zone:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.teaser-placeholder {
  aspect-ratio: 2.2 / 1; max-width: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  background: var(--panel-3); background-image: var(--preview-pattern);
  border: 1px dashed var(--rule);
}
.teaser-k { font-size: 16px; font-weight: 500; color: var(--ink-soft); }
.teaser-sub { font-size: 13px; color: var(--ink-dim); }

/* ---------- Layout: contents bar + content ---------- */

/* The content column is centred in the window, and the contents bar sits
   --toc-gap to its left, in the same grid cell. When the window is too narrow,
   the bar stays at the page edge and the gap shrinks to 32px; narrower still,
   the column moves right just enough to clear the bar. */
.layout {
  --toc-w: 240px;
  --toc-gap: 128px;
  --toc-space: calc(var(--toc-w) + 32px);
  --content-w: 1120px;
  display: grid; grid-template-columns: minmax(0, 1fr); align-items: start;
}
.layout > .toc, .layout > .content { grid-area: 1 / 1; }
.content {
  display: flex; flex-direction: column; gap: 88px; min-width: 0;
  width: min(var(--content-w), 100% - var(--toc-space));
  margin-left: max(var(--toc-space), (100% - var(--content-w)) / 2);
}

/* Once stuck, the bar sits centred in the window: --toc-top is set by main.js */
.toc {
  width: var(--toc-w); justify-self: start;
  margin-left: max(0px, (100% - var(--content-w)) / 2 - var(--toc-w) - var(--toc-gap));
  position: sticky; top: calc(env(safe-area-inset-top, 0px) + var(--toc-top, 24px));
  max-height: calc(100vh - 48px); overflow-y: auto; padding-bottom: 8px;
}
.toc ol { list-style: none; margin: 0; padding: 0; }
.toc-label {
  margin-bottom: 10px; padding-inline: 12px;
  font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-dim);
}
.toc-top > li + li { margin-top: 2px; }
.toc a {
  display: block; padding: 7px 12px; border-left: 2px solid transparent;
  font-size: 14.5px; font-weight: 500; line-height: 1.35;
  text-decoration: none; color: var(--ink-soft);
  transition: background .15s, color .15s, border-color .15s;
}
.toc a:hover { color: var(--ink); background: var(--panel); }
.toc a.is-active { color: var(--ink); background: var(--panel); border-left-color: var(--accent); }
.toc a.is-parent { color: var(--ink); }
/* Subsections (skills, Experiments questions) are indented under their section */
.toc .toc-sub { margin-left: 12px; padding-left: 4px; border-left: 1px solid var(--rule); }
.toc-sub a { padding: 5px 12px; font-size: 13.5px; font-weight: 400; }
.toc a:focus-visible { outline-offset: -2px; }
/* A section with subsections: the arrow sits over the right end of its link.
   The list is open while the reader is in that section (site.js) */
.toc-row { position: relative; }
.toc-row > a { padding-right: 38px; }
.toc-toggle {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 28px; height: 28px; display: grid; place-items: center; padding: 0;
  border: 0; border-radius: 4px; background: none; color: var(--ink-dim); cursor: pointer;
  transition: color .15s, background .15s;
}
.toc-toggle:hover { color: var(--accent); background: var(--panel-2); }
.toc-toggle svg { transform: rotate(90deg); transition: transform .2s ease; }
.has-sub.is-collapsed .toc-toggle svg { transform: none; }
.toc-sub-wrap { display: grid; grid-template-rows: 1fr; overflow: hidden; margin: 2px 0 8px; }
.toc-sub-wrap > .toc-sub { min-height: 0; }
.has-sub.is-collapsed .toc-sub-wrap { grid-template-rows: 0fr; margin-block: 0; visibility: hidden; }
.toc.toc-ready .toc-sub-wrap { transition: grid-template-rows .25s ease, margin .25s ease, visibility 0s; }
.toc.toc-ready .has-sub.is-collapsed .toc-sub-wrap { transition: grid-template-rows .25s ease, margin .25s ease, visibility 0s .25s; }
/* Reading a subsection whose list is closed: mark its section instead */
.toc .has-sub.is-collapsed a.is-parent { background: var(--panel); border-left-color: var(--accent); }

/* ---------- Top-level page sections ---------- */

.page-section { scroll-margin-top: calc(var(--strip-h) + 20px); min-width: 0; }
.psec-head { margin-bottom: 32px; }
/* Section and subsection titles: a rule runs from the text to the right edge,
   centred on the text's height (dark for sections, light for subsections) */
.psec-head h2, .subsection h3 { display: flex; align-items: center; gap: 16px; }
.psec-head h2::after, .subsection h3::after { content: ""; flex: 1 0 24px; height: 1px; }
.psec-head h2::after { background: var(--rule-strong); }
.subsection h3::after { background: var(--rule); }
.psec-head h2 {
  font-family: var(--sans); font-weight: 600;
  font-size: clamp(24px, 2.6vw, 28px); line-height: 1.25; letter-spacing: -0.01em;
  margin: 0 0 18px; color: var(--ink); text-wrap: balance;
}
.lead { font-size: 17px; line-height: 1.7; color: var(--ink); margin: 0 0 14px; }
.lead:last-child { margin-bottom: 0; }
ol.lead { padding-left: 1.4em; }
ol.lead li + li { margin-top: 6px; }
.lead a { color: var(--accent); text-decoration: none; }
.lead a:hover { text-decoration: underline; }
/* A wide button at the end of a section (Benchmark: Browse all tasks;
   3D-printable Assets: Browse all asset models; Simulation: Simulation code) */
.section-cta { display: flex; justify-content: center; margin: 40px 0 0; }
.cta-btn {
  display: inline-block; text-align: center;
  width: min(100%, 440px); padding: 14px 28px; border-radius: 999px;
  background: var(--ink); color: var(--panel); text-decoration: none;
  font-size: 17px; font-weight: 600; line-height: 1.3;
  transition: background .15s;
}
.cta-btn:hover { background: var(--accent); }
.cta-arrow { display: inline-block; margin-left: 10px; transition: transform .15s; }
.cta-btn:hover .cta-arrow { transform: translateX(4px); }
/* Link not set yet (as on the cover's .link-btn) */
.cta-btn[data-placeholder] { background: var(--ink-soft); }
.cta-btn[data-placeholder]:hover { background: var(--ink-dim); }
/* An opening question set off above a section's lead paragraph */
.lead-question {
  font-size: 22px; line-height: 1.45; font-weight: 500; color: var(--ink);
  margin: 0 0 16px; padding-left: 18px; border-left: 3px solid var(--rule-strong);
}

/* Variation type legend (task page) */
.legend { display: flex; flex-direction: column; gap: 8px; font-size: 14.5px; color: var(--ink-soft); }
.legend-row { display: flex; gap: 10px; align-items: baseline; }
.legend .type-badge { flex-shrink: 0; }

/* Placeholder sections */
.empty-state {
  padding: 32px 28px; background: var(--panel-3); border: 1px dashed var(--rule); border-radius: var(--radius);
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
}
.empty-tag {
  font-size: 12px; font-weight: 500; line-height: 1.2;
  color: var(--warm); border: 1px solid currentColor; border-radius: 999px; padding: 3px 9px;
}
.empty-state p { font-size: 15px; line-height: 1.6; color: var(--ink-soft); margin: 0; max-width: 62ch; }

/* BibTeX */
.bib { position: relative; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
.bib pre {
  margin: 0; padding: 20px 22px; overflow-x: auto;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.7; color: var(--ink);
}
.bib-copy {
  position: absolute; top: 10px; right: 10px;
  padding: 4px 12px; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); color: var(--ink-soft);
  font-size: 13px; font-weight: 500;
  transition: color .15s, border-color .15s;
}
.bib-copy:hover { color: var(--accent); border-color: var(--accent); }

/* Subsection titles (Experiments questions) */
.sec-head { margin-bottom: 22px; }
.subsection h3 {
  font-family: var(--sans); font-weight: 600;
  font-size: clamp(20px, 2vw, 22px); line-height: 1.3; letter-spacing: -0.01em;
  margin: 0 0 10px; color: var(--ink); text-wrap: balance;
}

/* Links to the All tasks page */
.all-tasks-btn {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 6px;
  padding: 8px 16px; border: 1px solid var(--accent); border-radius: 999px;
  color: var(--accent); text-decoration: none;
  font-size: 15px; font-weight: 500; line-height: 1.3;
  transition: background .15s, color .15s;
}
.all-tasks-btn:hover { background: var(--accent); color: var(--panel); }

/* ---------- Task cards ---------- */

.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; }
.card-grid.is-wide { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
.tcard {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
}
/* Thumbnail: 16:9, the whole frame plays the task's video */
.tcard-thumb {
  aspect-ratio: 16 / 9; width: 100%; padding: 0; position: relative; overflow: hidden;
  display: grid; place-items: center;
  background: var(--video-bg); color: var(--ink-soft); border-bottom: 1px solid var(--rule);
}
button.tcard-thumb { cursor: zoom-in; }
.tcard-thumb:focus-visible { outline-offset: -3px; }
.tcard-thumb > img, .tcard-thumb > video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none;
}
.tcard-thumb > svg { width: 72%; height: 72%; color: var(--accent); stroke-width: 1.6; stroke-linecap: square; }
@media (prefers-reduced-motion: no-preference) {
  .tcard-thumb > img, .tcard-thumb > video { transition: transform .35s ease; }
  button.tcard-thumb:hover > img, button.tcard-thumb:hover > video { transform: scale(1.03); }
}
.play-badge {
  /* In the bottom-left corner, clear of the middle of the picture */
  position: absolute; left: 8px; bottom: 8px; z-index: 1;
  width: 32px; height: 32px; display: grid; place-items: center; padding-left: 2px;
  background: var(--vidmeta-bg); border: 1px solid var(--rule); border-radius: 50%; color: var(--ink);
  transition: color .15s, border-color .15s;
}
button:hover > .play-badge { color: var(--accent); border-color: var(--accent); }
.play-badge .play-tri { border-width: 5px 0 5px 8px; }
.play-tri {
  width: 0; height: 0; border-style: solid;
  border-width: 6px 0 6px 10px; border-color: transparent transparent transparent currentColor;
}
/* Caption bar under the thumbnail: the name (a link to the task page while
   task pages are shown) */
.tcard-cap {
  flex: 1; padding: 11px 14px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  color: var(--ink); text-decoration: none;
  transition: background .15s, color .15s;
}
a.tcard-cap:hover { background: var(--panel-3); }
.tcard-cap:focus-visible { outline-offset: -3px; }
.tcard-name {
  margin: 0; font-family: var(--sans); font-weight: 600;
  font-size: 15px; line-height: 1.35; letter-spacing: -0.005em; color: inherit; text-wrap: balance;
}
.tcard-arrow { flex-shrink: 0; color: var(--accent); font-size: 18px; line-height: 1; }
@media (prefers-reduced-motion: no-preference) {
  .tcard-arrow { transition: transform .15s ease; }
  .tcard-cap:hover .tcard-arrow { transform: translateX(3px); }
}
a.tcard-cap:hover .tcard-name { color: var(--accent); }
.tcard-link {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border: 1px solid var(--rule); border-radius: 4px; background: var(--panel);
  color: var(--accent); text-decoration: none; font-size: 13.5px; font-weight: 500; line-height: 1.4;
  transition: border-color .15s, background .15s;
}
.tcard-link:hover { border-color: var(--accent); background: var(--panel-3); }

/* A card's videos, opened in the grid below its row (js/site.js) */
.tcard.is-open { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.tcard.is-open .play-badge { display: none; }
.tcard-expand {
  grid-column: 1 / -1; min-width: 0;
  padding: 14px 16px 10px;
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
  /* Clear the sticky contents strip / pinned bar when scrolled into view */
  scroll-margin: calc(var(--strip-h) + 72px) 0 24px;
}
.tx-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.tx-title { flex: 1; min-width: 0; margin: 0; font-size: 16px; font-weight: 600; line-height: 1.35; color: var(--ink); }
/* The panel's main action: a filled button, so it reads first */
.tx-details {
  flex-shrink: 0; display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 16px; border: 1px solid var(--accent); border-radius: 999px;
  background: var(--accent); color: var(--panel); text-decoration: none;
  font-size: 14.5px; font-weight: 600; line-height: 1.3;
  transition: background .15s, border-color .15s;
}
.tx-details:hover {
  background: color-mix(in srgb, var(--accent) 82%, var(--ink));
  border-color: color-mix(in srgb, var(--accent) 82%, var(--ink));
}
@media (prefers-reduced-motion: no-preference) {
  .tx-arrow { transition: transform .15s ease; }
  .tx-details:hover .tx-arrow { transform: translateX(3px); }
}
.tx-close {
  width: 32px; height: 32px; flex-shrink: 0; display: grid; place-items: center; padding: 0 0 2px;
  border: 1px solid var(--rule); border-radius: 50%; background: var(--panel);
  font-size: 20px; line-height: 1; color: var(--ink-soft);
  transition: color .15s, border-color .15s;
}
.tx-close:hover { color: var(--accent); border-color: var(--accent); }
/* Big, but short enough that the video fits in the window with room around it */
.tcard-expand { --tx-w: min(calc(58vh * 16 / 9), 960px); }
.tcard-expand .player { width: 100%; max-width: var(--tx-w); margin: 0 auto; }
/* The task's specs under its videos, as wide as the player */
.tcard-expand .spec { max-width: var(--tx-w); margin: 20px auto 8px; }
/* A card's panel: the title and close button line up with the video's edges */
.task-expand .tx-head { max-width: var(--tx-w); margin: 0 auto 14px; }
.task-expand .tx-title { font-size: 20px; letter-spacing: -0.01em; }

/* ---------- Benchmark section (js/benchmark.js) ---------- */

/* Pause icon (two bars), or a play triangle once paused */
.reel-ico {
  flex-shrink: 0; width: 10px; height: 11px;
  border-style: solid; border-color: currentColor; border-width: 0 3px;
}
.is-paused > .reel-ico {
  width: 0; height: 0; margin-left: 2px;
  border-width: 5.5px 0 5.5px 9px; border-color: transparent transparent transparent currentColor;
}

/* Clips played one after another: the next fades in over the last */
.reel { position: relative; overflow: hidden; background: var(--video-bg); }
.reel > video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; pointer-events: none; transition: opacity .4s ease;
}
.reel > video.on { opacity: 1; pointer-events: auto; }
/* A tile's pause button, in the bottom-left corner of its video */
.reel-btn {
  position: absolute; left: 8px; bottom: 8px; z-index: 2;
  width: 30px; height: 30px; display: grid; place-items: center; padding: 0;
  background: var(--vidmeta-bg); border: 1px solid var(--rule); border-radius: 50%; color: var(--ink);
  transition: color .15s, border-color .15s;
}
.reel-btn:hover { color: var(--accent); border-color: var(--accent); }

/* Skill tiles: one per skill, four a row. The video fills a square tile,
   with a tick per clip along its top and the skill's label, name, and task
   count over its foot (a task card has its name in a bar under its video).
   The parts share one grid cell, the text laid over the video with z-index.
   Only the task count (underlined) and the pause button respond to clicks. */
.skill-tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
.skill-tile {
  position: relative; display: grid; grid-template: minmax(0, 1fr) / minmax(0, 1fr);
  aspect-ratio: 1; min-width: 0; overflow: hidden;
  background: var(--video-bg); border: 1px solid transparent; border-radius: var(--radius);
}
.skill-tile > * { grid-area: 1 / 1; }
.skill-tile-media { position: relative; display: grid; place-items: center; }
.skill-tile-media::after {
  content: ""; position: absolute; inset: 0; background: var(--tile-scrim); pointer-events: none;
}
.skill-tile .reel > video.on { pointer-events: none; }
.skill-tile .reel-btn { left: auto; bottom: auto; top: 22px; right: 8px; }
/* One tick per clip, filling while it plays */
.skill-tile-ticks { align-self: start; z-index: 1; display: flex; gap: 3px; padding: 10px 10px 0; }
.skill-tile-ticks > span { flex: 1; height: 3px; border-radius: 2px; background: var(--on-dark-track); overflow: hidden; }
.skill-tile-ticks i { display: block; width: 0; height: 100%; background: var(--on-dark); transition: width .25s linear; }
.skill-tile-ticks.no-anim i { transition: none; }
.skill-tile-body {
  align-self: end; z-index: 1; display: flex; flex-direction: column; gap: 2px; padding: 12px 14px 13px;
  color: var(--on-dark); text-shadow: 0 1px 3px var(--scrim-shadow);
}
.skill-tile-label { font-size: 13px; font-weight: 500; color: var(--on-dark); }
.skill-tile-name { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.3; color: var(--on-dark); text-wrap: balance; }
.skill-tile-count {
  align-self: flex-start; margin-top: 6px; font-size: 13.5px; font-weight: 600; color: var(--on-dark);
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px;
}
.skill-tile-count:hover { text-decoration-thickness: 2px; }
.skill-tile-count:focus-visible { outline: 2px solid var(--on-dark); outline-offset: 2px; border-radius: 2px; }
.skill-tile-arrow { display: inline-block; margin-left: .3em; transition: transform .15s; }
.skill-tile-count:hover .skill-tile-arrow { transform: translateX(3px); }

/* The paragraph between the tiles and the long-horizon band (index.html) */
.skill-tiles + .lead { margin-top: 32px; }

/* The Dexterous Long-Horizon Tasks' cards, under the tiles and the
   paragraph that introduces them (no box or visible heading) */
.lh-band { margin-top: 36px; }
.lead + .lh-band { margin-top: 18px; }
.card-grid.lh-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; }
.lh-band .tcard-cap { padding: 9px 12px 6px; }
.lh-band .tcard-name { font-size: 14px; }
.lh-band .tcard .skill-chips { padding: 0 12px 11px; }
/* The ids of the skills a task combines */
.skill-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.skill-chip {
  font-family: var(--mono); font-size: 11.5px; line-height: 1; padding: 4px 6px;
  background: var(--chip-bg); border-radius: 4px; color: var(--accent);
}

/* ---------- Simulation section (js/sim.js) ---------- */
/* The list of clips on the left, as tall as the player beside it and
   scrolling on its own */
.sim-player {
  display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 20px;
  padding: 16px; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
}
.sim-list-wrap { position: relative; min-width: 0; }
.sim-list {
  position: absolute; inset: 0; margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 2px; overflow-y: auto; overscroll-behavior: contain;
}
.sim-item {
  position: relative; width: 100%; display: grid; grid-template-columns: 120px minmax(0, 1fr); align-items: center; gap: 12px;
  padding: 6px; border-radius: 4px; text-align: left; color: var(--ink-soft);
  transition: background .15s, color .15s;
}
.sim-item:hover { background: var(--panel-3); color: var(--ink); }
.sim-item[aria-current="true"] { background: var(--panel-2); color: var(--ink); }
.sim-item:focus-visible { outline-offset: -2px; }
.sim-thumb { position: relative; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 3px; background: var(--video-bg); }
.sim-thumb > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.sim-item-text { display: flex; flex-direction: column; min-width: 0; font-size: 13.5px; line-height: 1.35; }
.sim-item[aria-current="true"] .sim-item-name { font-weight: 600; }
.sim-item-var { font-size: 12.5px; color: var(--ink-dim); }
/* How far the current clip has played */
.sim-prog { position: absolute; left: 6px; right: 6px; bottom: 1px; height: 2px; }
.sim-prog i { display: block; width: 0; height: 100%; background: var(--accent); transition: width .25s linear; }
.sim-stage { min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.sim-video { aspect-ratio: 16 / 9; border-radius: 4px; }
.sim-now { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; min-height: 24px; font-size: 15px; line-height: 1.4; }
.sim-now-cap { min-width: 0; }
.sim-name { font-weight: 600; color: var(--ink); text-decoration: none; transition: color .15s; }
a.sim-name:hover { color: var(--accent); }
.sim-var { margin-left: 8px; color: var(--ink-soft); }
.sim-count { flex-shrink: 0; font-size: 13.5px; color: var(--ink-dim); font-variant-numeric: tabular-nums; }

/* ---------- 3D-printable assets: variations (js/variations.js) ---------- */
/* One band per type of variation: its heading and a still per example on the
   left, one player on the right. --var-c is the type's color: --warm for
   visual + behavioral, --accent for behavioral. */
.var-bands { display: flex; flex-direction: column; gap: 20px; }
.var-band {
  --var-c: var(--accent); --var-label-bg: var(--b-label-bg);
  display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 8fr); gap: 28px; align-items: center;
  padding: 22px; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
}
.var-band.vb { --var-c: var(--warm); --var-label-bg: var(--vb-label-bg); }
.var-head { padding-left: 14px; border-left: 3px solid var(--var-c); }
.var-title { margin: 0 0 2px; font-size: 19px; font-weight: 600; line-height: 1.35; color: var(--ink); text-wrap: balance; }
.var-sub { margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink-soft); }
/* The still of the clip playing is outlined, with a bar along its foot */
.var-stills { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin-top: 18px; }
.var-still {
  position: relative; aspect-ratio: 16 / 9; padding: 0; overflow: hidden;
  background: var(--panel); border: 1px solid var(--rule); border-radius: 4px;
  opacity: .65; transition: opacity .15s, border-color .15s;
}
.var-still:hover, .var-still[aria-current="true"] { opacity: 1; }
.var-still[aria-current="true"] { border-color: var(--var-c); box-shadow: 0 0 0 1px var(--var-c); }
.var-still > video, .var-still > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
/* A still's data-label, sized to the still so it fits on one line; the
   force-curve images leave their top ~40% empty for it */
.var-still:has(.var-label) { container-type: inline-size; }
.var-label {
  position: absolute; top: 5px; left: 5px; pointer-events: none;
  padding: .15em .4em; border-radius: 3px; background: var(--var-label-bg);
  font-size: 13px; font-size: clamp(9px, 11cqi, 15px);
  font-weight: 600; line-height: 1.25; color: var(--ink); white-space: nowrap;
}
.var-prog { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; }
.var-prog i { display: block; width: 0; height: 100%; background: var(--var-c); transition: width .25s linear; }
/* The renders have white backgrounds, so the player is white too */
.var-player { aspect-ratio: 16 / 9; border-radius: 4px; }
.var-player.reel { background: var(--panel); }

/* ---------- Real-world setup (index.html; js/main.js plays the clips) ---------- */
/* One card per step, four a row: its clip (or image) over the step's number
   and sentence. The rows' cards are as tall as their longest sentence. */
.setup-steps {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px;
  margin: 0; padding: 0; list-style: none;
}
.setup-step {
  display: flex; flex-direction: column; min-width: 0; overflow: hidden;
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
}
.setup-media { position: relative; aspect-ratio: 16 / 9; background: var(--video-bg); }
/* The board photo has a transparent background: shown whole on the card's white */
.setup-media:has(> img) { background: var(--panel); }
.setup-media > img { position: absolute; inset: 0; width: 100%; height: 100%; padding: 8px 8px 0; object-fit: contain; }
/* The pause button in the top-right corner, clear of the marker and plot at the foot */
.setup-media .reel-btn { left: auto; bottom: auto; top: 8px; right: 8px; }
.setup-text { padding: 12px 14px 14px; }
.setup-num { display: block; margin-bottom: 4px; font-size: 17px; font-weight: 600; line-height: 1.3; color: var(--accent); }
.setup-text p { margin: 0; font-size: 15px; line-height: 1.5; color: var(--ink); }
@media (max-width: 860px) { .setup-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px) { .setup-steps { grid-template-columns: minmax(0, 1fr); } }

/* ---------- Video lightbox ---------- */

html.has-lightbox { overflow: hidden; }
.lightbox {
  padding: 0; border: 0; background: transparent; color: var(--on-dark);
  max-width: none; max-height: none; overflow: visible;
  width: min(1280px, calc(100vw - 32px), calc((100vh - 150px) * 16 / 9));
}
.lightbox::backdrop { background: var(--backdrop); }
.lb-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; margin-bottom: 10px; }
.lb-text { min-width: 0; }
.lb-title { margin: 0; font-size: 17px; font-weight: 600; line-height: 1.35; color: var(--on-dark); }
.lb-close {
  flex-shrink: 0; width: 36px; height: 36px; display: grid; place-items: center;
  background: var(--vidmeta-bg); color: var(--ink); border-radius: 50%;
  font-size: 22px; line-height: 1; transition: color .15s;
}
.lb-close:hover { color: var(--accent); }
.lb-close:focus-visible { outline-color: var(--on-dark); }
.lb-video { aspect-ratio: 16 / 9; background: var(--ink); border-radius: 4px; overflow: hidden; }
.lb-video video { width: 100%; height: 100%; object-fit: contain; }
.lb-foot { margin-top: 12px; display: flex; justify-content: flex-end; }

/* ---------- All tasks page ---------- */

.subpage { max-width: 1280px; }
.sub-head { margin-bottom: 28px; }
.sub-head .all-tasks-btn { margin: 0 0 18px; }
/* Pinned bar on the All tasks page, shown once #page-back scrolls away.
   visibility keeps it out of the tab order while it's hidden. */
.scroll-bar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 30;
  padding: calc(env(safe-area-inset-top, 0px) + 10px) 24px 10px;
  background: var(--panel); border-bottom: 1px solid var(--rule);
  visibility: hidden; transform: translateY(-100%);
  transition: transform .2s ease, visibility 0s linear .2s;
}
.scroll-bar.is-shown { visibility: visible; transform: none; transition: transform .2s ease; }
.scroll-bar-inner { max-width: 1280px; margin: 0 auto; display: flex; align-items: center; gap: 16px; }
.scroll-bar-title { font-size: 15px; font-weight: 600; color: var(--ink); }
.scroll-bar .all-tasks-btn { margin-top: 0; padding-block: 6px; }
.sub-head h1, .task-head h1 {
  font-family: var(--sans); font-weight: 700;
  font-size: clamp(28px, 3.4vw, 36px); line-height: 1.2; letter-spacing: -0.02em;
  margin: 0 0 12px; color: var(--ink); text-wrap: balance;
}
.filters {
  margin-bottom: 24px; padding: 18px 20px;
  display: flex; flex-direction: column; gap: 14px;
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
}
.search { position: relative; }
.search-ico {
  position: absolute; left: 12px; top: 50%; width: 16px; height: 16px; transform: translateY(-50%);
  fill: none; stroke: var(--ink-dim); stroke-width: 1.6; stroke-linecap: round; pointer-events: none;
}
.search input {
  width: 100%; padding: 10px 12px 10px 38px;
  font: inherit; font-size: 15px; color: var(--ink);
  background: var(--panel-3); border: 1px solid var(--rule); border-radius: var(--radius);
  transition: border-color .15s, background .15s;
}
.search input::placeholder { color: var(--ink-dim); }
.search input:hover { border-color: var(--ink-dim); }
.search input:focus { border-color: var(--accent); background: var(--panel); }
.opt-count { margin-left: 6px; font-size: 12px; font-weight: 400; color: var(--ink-dim); font-variant-numeric: tabular-nums; }
.results { margin: 0; font-size: 13.5px; color: var(--ink-soft); }
#no-results { margin-top: 8px; }

/* ---------- Task page ---------- */

.toc-backs { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin: 0 0 24px 12px; }
/* Same look as .all-tasks-btn, restated here because the contents bar's
   own link styles (.toc a) would otherwise win */
.toc a.toc-back {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px; border: 1px solid var(--accent); border-radius: 999px; background: none;
  color: var(--accent); font-size: 14px; font-weight: 500;
  transition: background .15s, color .15s;
}
.toc a.toc-back:hover { background: var(--accent); color: var(--panel); }

.task-head { margin-bottom: 24px; }
.task-kicker {
  margin: 0 0 8px; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 0;
  font-size: 14px; font-weight: 500; color: var(--ink-soft);
}
.task-kicker > span:not(.task-id)::before { content: "·"; margin: 0 8px; color: var(--ink-dim); }
.task-id { margin-left: 6px; font-family: var(--mono); font-size: 13px; color: var(--ink); }

/* Video player: every video of the task, stepped through below the frame */
.player { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; }
.task-video {
  aspect-ratio: 16 / 9;
  background: var(--video-bg); border-radius: 4px;
  position: relative; display: grid; place-items: center; overflow: hidden;
  color: var(--ink-soft);
}
.task-video .slide { position: absolute; inset: 0; display: grid; place-items: center; }
.task-video .slide > svg { width: 72%; height: 72%; color: var(--accent); stroke-width: 1.6; stroke-linecap: square; }
.task-video .slide > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: var(--video-bg); }
.vidmeta {
  position: absolute; bottom: 6px; right: 6px; pointer-events: none;
  font-size: 11.5px; font-weight: 500; color: var(--accent);
  background: var(--vidmeta-bg); padding: 2px 5px;
}
.vidmeta:empty { display: none; }
/* Several videos: arrows on the video, and under it one dot per video */
.vstage { position: relative; }
.vnav {
  position: absolute; top: 50%; z-index: 1; transform: translateY(-50%);
  width: 40px; height: 40px; display: grid; place-items: center; padding: 0 0 3px;
  background: var(--vidmeta-bg); border: 1px solid var(--rule); border-radius: 50%;
  color: var(--ink); font-size: 24px; line-height: 1; cursor: pointer;
  transition: color .15s, border-color .15s;
}
.vnav:hover { color: var(--accent); border-color: var(--accent); }
.vnav.prev { left: 12px; }
.vnav.next { right: 12px; }
.vstep { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; }
/* Each dot is a 24px button with an 8px dot drawn in its middle; the current one is filled */
.vstep-dot { width: 24px; height: 24px; display: grid; place-items: center; padding: 0; border: 0; border-radius: 50%; background: none; cursor: pointer; }
.vstep-dot::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: var(--rule-strong); opacity: 0.22; transition: background .15s, opacity .15s, transform .15s;
}
.vstep-dot:hover::before { opacity: 0.5; }
.vstep-dot[aria-current="true"]::before { background: var(--accent); opacity: 1; transform: scale(1.15); }
/* Switching videos: the new slide waits beside the frame, then both slide
   over together (the reduced-motion rule makes this an instant swap) */
.task-video .slide { transition: transform .45s cubic-bezier(.4, 0, .2, 1); }
.task-video .slide.wait-next, .task-video .slide.leave-prev { transform: translateX(100%); }
.task-video .slide.wait-prev, .task-video .slide.leave-next { transform: translateX(-100%); }

/* Task specs: description, skills, success criteria. A bold label beside
   each text (above it on phones), with no table around them */
.spec { display: grid; grid-template-columns: 170px minmax(0, 1fr); gap: 18px 20px; align-items: baseline; }
.spec .rk { font-size: 17px; font-weight: 600; line-height: 1.5; color: var(--spec-label); }
.spec .rv { font-size: 15.5px; line-height: 1.6; color: var(--ink); }

.fbtn {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 6px 10px; text-decoration: none; white-space: nowrap;
  background: var(--panel); border: 1px solid var(--rule); border-radius: 4px; color: var(--ink);
  font-family: var(--mono);
  font-size: 12px;
  transition: border-color .12s, background .12s;
}
.fbtn:hover { border-color: var(--accent); background: var(--panel-3); }
.fbtn.print { border-color: color-mix(in srgb, var(--accent) 40%, var(--rule)); }
.fbtn-ico { color: var(--accent); }
.fbtn.sm { flex: 1; justify-content: center; gap: 4px; padding: 4px 6px; font-size: 11.5px; }

/* Skills */
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  font-size: 13.5px; line-height: 1.3;
  padding: 4px 12px; align-self: flex-start; text-decoration: none;
  border: 1px solid var(--rule); border-radius: 999px; color: var(--ink-soft); background: transparent;
  transition: background .15s, border-color .15s, color .15s;
}
a.chip:hover { color: var(--accent); border-color: var(--accent); }

/* Variation axis groups */
.variations { display: flex; flex-direction: column; gap: 16px; }
/* Left edge in the variation type's color, matching its badge */
.vgroup {
  background: var(--panel); border: 1px solid var(--rule); border-left: 3px solid var(--ink-dim);
  border-radius: var(--radius); overflow: hidden;   /* keeps the header wash inside the corners */
}
.vgroup.b { border-left-color: var(--accent); }
.vgroup.vb { border-left-color: var(--warm); }
/* Header row washed in the type's color */
.vgroup.b .vgroup-head { background: var(--b-wash); }
.vgroup.vb .vgroup-head { background: var(--vb-wash); }
.vgroup-head {
  padding: 10px 14px; display: flex; justify-content: space-between; align-items: center;
  gap: 8px 12px; flex-wrap: wrap; border-bottom: 1px solid var(--rule);
}
.vgroup-head:last-child { border-bottom: 0; }
.vgroup-name { font-weight: 500; font-size: 15px; letter-spacing: -0.005em; color: var(--ink); }
.vgroup-meta { display: inline-flex; align-items: center; gap: 10px; }
.vgroup-empty { margin: 0; padding: 12px 14px; font-size: 14px; color: var(--ink-soft); }
.type-badge {
  font-size: 12px; font-weight: 500; line-height: 1.2; white-space: nowrap;
  padding: 3px 9px; border: 1px solid currentColor; border-radius: 999px;
}
.type-badge.b { color: var(--accent); }
.type-badge.vb { color: var(--warm); }
.vgroup-count { font-family: var(--mono); font-size: 12px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.vgrid {
  padding: 12px 14px 14px;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 16px 10px;
}
.vcell { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.vthumb {
  aspect-ratio: 4 / 3; position: relative; overflow: hidden; border-radius: 4px;
  border: 1px solid var(--rule); background: var(--video-bg); background-image: var(--preview-pattern);
  display: grid; place-items: center;
}
.vthumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.vthumb-idx {
  font-size: 16px; font-weight: 500; color: var(--ink-dim); font-variant-numeric: tabular-nums;
}
/* Images have transparent backgrounds: no fill or frame behind them */
.vthumb.has-image { background: none; border-color: transparent; }
.vthumb.has-image .vthumb-idx { display: none; }
/* Video variant: plays in the lightbox */
.vthumb.is-video { width: 100%; padding: 0; background-image: none; cursor: zoom-in; }
.vthumb.is-video:focus-visible { outline-offset: -3px; }
.vthumb.is-video > img, .vthumb.is-video > video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none;
}
.vthumb.is-video .play-badge { left: 6px; bottom: 6px; width: 28px; height: 28px; }
.vtile {
  min-height: 48px; padding: 9px 12px;
  display: flex; flex-direction: column; justify-content: center;
  background: var(--panel-3); border: 1px solid var(--rule); border-radius: 4px;
}
.vcell:not(.is-text) .vcell-label { text-align: center; }
.vcell-label { font-size: 14px; line-height: 1.3; color: var(--ink); }
.vcell-sub {
  display: block; margin-top: 2px;
  font-family: var(--mono);
  font-size: 11.5px; color: var(--accent); font-variant-numeric: tabular-nums;
}
.vcell-part { margin-top: 2px; font-size: 12.5px; line-height: 1.3; color: var(--ink-soft); }
.vcell-actions { display: flex; gap: 4px; }

/* ---------- Spec tables (task specs, slicer settings) ---------- */

.crit-spec {
  display: grid; grid-template-columns: 160px minmax(0, 1fr);
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
}
.crit-spec > div { padding: 12px 14px; border-bottom: 1px solid var(--rule); }
.crit-spec > div:nth-last-child(-n+2) { border-bottom: 0; }
.crit-spec .rk {
  background: var(--panel-3); border-right: 1px solid var(--rule);
  font-size: 14px; font-weight: 500; line-height: 1.5; color: var(--ink-soft);
}
.crit-spec .rv { font-size: 15px; line-height: 1.5; color: var(--ink); }

/* ---------- 3D printing instructions ---------- */

.print-general { max-width: 68ch; margin-bottom: 20px; font-size: 15.5px; line-height: 1.65; color: var(--ink); }
.print-blocks { display: flex; flex-direction: column; gap: 16px; }
.print-block h3.vgroup-name { margin: 0; font-family: var(--sans); font-weight: 600; line-height: 1.35; }
.print-body { padding: 14px 16px 18px; display: flex; flex-direction: column; gap: 10px; }
.print-k { margin: 8px 0 0; font-size: 14px; font-weight: 600; line-height: 1.4; color: var(--ink); }
.print-k:first-child { margin-top: 0; }
.print-body .prose { font-size: 15px; line-height: 1.6; color: var(--ink); }
.print-parts { list-style: none; margin: 0; padding: 0; border: 1px solid var(--rule); border-radius: var(--radius); }
.print-part {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; gap: 12px; align-items: center;
  padding: 8px 12px;
}
.print-part + .print-part { border-top: 1px solid var(--rule); }
.print-part-img { width: 44px; height: 33px; display: grid; place-items: center; }
.print-part-img img { max-width: 100%; max-height: 100%; object-fit: contain; }
.print-part-text { display: flex; flex-direction: column; min-width: 0; }
.print-part-name { font-size: 14.5px; font-weight: 500; line-height: 1.35; color: var(--ink); }
.print-part-use { font-size: 12.5px; line-height: 1.4; color: var(--ink-soft); }
.print-part-note { margin-top: 2px; font-size: 13px; line-height: 1.45; color: var(--ink); }
.print-part-files { display: flex; }
.print-steps {
  margin: 0; padding-left: 22px; display: flex; flex-direction: column; gap: 6px;
  font-size: 15px; line-height: 1.55; color: var(--ink);
}
.print-steps li::marker { color: var(--ink-dim); font-variant-numeric: tabular-nums; }

/* File not available yet (no path in the task config) */
.fbtn.is-off,
.fbtn.is-off:hover {
  color: var(--ink-dim); background: transparent;
  border-color: var(--rule); border-style: dashed; cursor: default;
}
.fbtn.is-off .fbtn-ico { color: var(--ink-dim); }

.sec-empty { font-size: 15px; color: var(--ink-soft); margin: 0; }

/* ---------- Policy rollout viewer (inside Experiments) ---------- */
/* Its option buttons (.ro-row, .ro-k, .ro-opt) are also used by the skill
   filter on the All tasks page. */

/* A titled block inside a page section, headed like a skill section */
.subsection { margin-top: 56px; scroll-margin-top: calc(var(--strip-h) + 20px); min-width: 0; }
/* A little more space from one subsection to the next subsection's title */
.subsection + .subsection { margin-top: 68px; }

/* "What is challenging for vision-based policies…": above the table, the
   challenges as numbered cards in two labeled boxes, one per category: 1–4
   in a box tinted --contact, whose label is a sentence with the phrase
   they share (.contact-term) highlighted in that color, and Lack of memory
   apart in a dashed box (.is-apart, with a grey number). The labels share a
   row (a subgrid), so the cards line up however the labels wrap. The columns
   are sized so every card is as wide. */
.contact-term { font-weight: 600; color: var(--contact); }
.challenge-cards {
  --pad: 8px; --gap: 8px; --split: 16px;
  --card: calc((100% - var(--split) - 3 * var(--gap) - 4 * var(--pad)) / 5);
  display: grid; grid-template-columns: calc(4 * var(--card) + 3 * var(--gap) + 2 * var(--pad)) minmax(0, 1fr);
  grid-template-rows: auto auto; gap: 0 var(--split); margin-top: 18px;
}
.challenge-group {
  display: grid; grid-row: span 2; grid-template-rows: subgrid; row-gap: 6px;
  padding: var(--pad); border-radius: calc(var(--radius) + 4px);
}
.challenge-group.is-contact { --n: 4; background: var(--contact-wash); }
.challenge-group.is-apart { outline: 1px dashed var(--ink-dim); outline-offset: -1px; }
.challenge-group-label {
  align-self: end; margin: 0; padding: 2px 6px 0;
  font-size: 15px; line-height: 1.45; color: var(--ink); text-wrap: balance;
}
.is-contact > .challenge-group-label { font-size: 18px; line-height: 1.4; }
.is-apart > .challenge-group-label { color: var(--ink-soft); }
.challenge-group > ol {
  display: grid; grid-template-columns: repeat(var(--n, 1), minmax(0, 1fr)); gap: var(--gap);
  margin: 0; padding: 0; list-style: none;
}
.challenge-card { padding: 12px 14px 13px; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
/* The number on a card and on its row's title */
.challenge-num {
  display: inline-grid; place-items: center; flex: none; width: 22px; height: 22px; border-radius: 50%;
  background: var(--contact-wash); font-size: 12.5px; font-weight: 600; line-height: 1; color: var(--contact);
  font-variant-numeric: tabular-nums; vertical-align: middle;
}
.is-apart .challenge-num { background: var(--panel); border: 1px dashed var(--ink-dim); color: var(--ink-dim); }
.challenge-card-name { display: block; margin: 8px 0 3px; font-size: 14.5px; font-weight: 600; line-height: 1.35; color: var(--ink); text-wrap: balance; }
.challenge-card-def { display: block; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); }
@media (max-width: 860px) {
  .challenge-cards { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; row-gap: var(--split); }
  .challenge-group { grid-row: auto; grid-template-rows: none; }
  .challenge-group, .challenge-group.is-contact { --n: 2; }
}
@media (max-width: 480px) {
  .challenge-group, .challenge-group.is-contact { --n: 1; }
}
/* "Do visual-tactile policies address…": the three findings as full-width
   white cards, one under another, each with its red number and name, its
   sentence, and its failure clips (.failure-clips) */
.tactile-cards { display: flex; flex-direction: column; gap: 16px; margin: 18px 0 0; padding: 0; list-style: none; }
/* --indent: the number's width and the gap after it, so the sentence and the
   clips start where the name does */
.tactile-card { --indent: 32px; min-width: 0; padding: 16px 18px 18px; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
.tactile-card-title {
  display: flex; align-items: flex-start; gap: 10px; margin: 0;
  font-size: 16.5px; font-weight: 600; line-height: 1.4; color: var(--ink);
}
.tactile-card-title > .challenge-num { margin-top: 1px; }
.tactile-card-text { margin: 6px 0 0 var(--indent); font-size: 15px; line-height: 1.6; color: var(--ink-soft); }
.tactile-card > .failure-clips { margin: 14px 0 0 var(--indent); }
.is-tactile .challenge-num { background: var(--tactile-wash); color: var(--tactile); }
@media (max-width: 640px) {
  .tactile-card { --indent: 0px; padding: 14px 12px 14px; }
}

/* ---------- Experiments: behavioral generalization ---------- */
/* One part per variation, headed like the asset variation bands (.var-head):
   a rule in the type's color, --accent for behavioral, --warm for visual +
   behavioral */
/* The same space above the first part (after the opening paragraph) as between parts */
.gen-parts { --gen-gap: 48px; display: flex; flex-direction: column; gap: var(--gen-gap); margin-top: var(--gen-gap); }
.gen-part { --var-c: var(--accent); min-width: 0; }
.gen-part.vb { --var-c: var(--warm); }
.gen-part > * { margin: 0; }
.gen-part > * + * { margin-top: 20px; }
.gen-part > p + p { margin-top: 12px; }
.gen-part .var-title { margin: 0; font-size: 20px; }
.gen-part > p { font-size: 16px; line-height: 1.7; color: var(--ink); }
/* Captions under a figure, and over a table */
.gen-part figcaption { margin: 10px 0 0; font-size: 13.5px; line-height: 1.55; color: var(--ink-soft); }
/* The figure (white background) in a white box */
.gen-fig { padding: 16px 20px; background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); }
.gen-fig img { display: block; width: 100%; height: auto; }
/* On narrow screens the figure keeps a readable width and scrolls sideways */
.gen-fig-scroll { overflow-x: auto; }
@media (max-width: 640px) {
  .gen-fig img { min-width: 620px; }
  .gen-part .var-title { font-size: 18px; }
}
/* A small table straight on the page, its title above */
.gen-table { max-width: 760px; margin-inline: auto; }
.gen-part .gen-table > figcaption { margin: 0 0 8px; text-align: center; font-size: 16px; font-weight: 600; color: var(--ink); }
.gen-table .rtable thead tr:first-child th:first-child, .gen-table .rtable tbody th { background: var(--bg); }
/* Example clips, one per method, four a row (two under 860px); each plays only
   when its own play button is pressed. --m is the method's color token. Each
   clip is two videos stacked (training condition on top, variation below):
   its policy label sits over it, and .gen-rows names the two halves down the
   left, turned to read upward. Each clip is a subgrid of the grid's label row
   and video row, so .gen-rows spans exactly the videos' height; its labels are
   positioned out of flow in a column two lines wide (--rows-fs), so they never
   size the grid. Under 860px the clips wrap to two rows, and .gen-rows.is-repeat
   labels the second. */
.gen-clips {
  --rows-fs: 14.5px;
  display: grid; grid-template-columns: calc(2.6 * var(--rows-fs)) repeat(4, minmax(0, 1fr));
  grid-template-rows: auto auto; column-gap: 12px;
}
.gen-clip { grid-row: 1 / span 2; display: grid; grid-template-rows: subgrid; margin: 0; min-width: 0; }
.gen-clip video { display: block; width: 100%; aspect-ratio: 640 / 964; background: var(--video-bg); border-radius: 4px; }
.gen-part .gen-clip figcaption {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center; column-gap: 7px;
  margin: 0 0 10px; font-size: 15px; line-height: 1.35; color: var(--ink-soft);
}
.gen-clip figcaption b { font-size: 18px; font-weight: 600; color: var(--ink); white-space: nowrap; }
.gen-clip figcaption b::before {
  content: ""; display: inline-block; width: 12px; height: 12px; margin-right: 7px;
  border-radius: 3px; background: var(--m);
}
/* One label per half of the videos, centred in it */
.gen-rows { grid-column: 1; grid-row: 2; position: relative; }
.gen-rows span {
  position: absolute; left: 0; right: 0; top: 0; height: 50%;
  display: grid; place-content: center;
  writing-mode: vertical-rl; transform: rotate(180deg);
  font-size: var(--rows-fs); font-weight: 500; line-height: 1.3; color: var(--ink);
  text-align: center; white-space: nowrap;
}
.gen-rows span + span { top: 50%; }
.gen-rows small { font-size: inherit; font-weight: 400; color: var(--ink-soft); }
.gen-rows.is-repeat { display: none; }
@media (max-width: 860px) {
  .gen-clips { grid-template-columns: calc(2.6 * var(--rows-fs)) repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(4, auto); column-gap: 10px; }
  .gen-clip:nth-of-type(n+3) { grid-row: 3 / span 2; }
  .gen-clip:nth-of-type(n+3) figcaption { margin-top: 20px; }
  .gen-rows.is-repeat { display: block; grid-row: 4; }
}
@media (max-width: 480px) {
  .gen-clips { --rows-fs: 12.5px; column-gap: 8px; }
  .gen-clip figcaption b { font-size: 16px; }
  .gen-part .gen-clip figcaption { font-size: 13.5px; }
}

/* The table under the cards, straight
   on the page: a row per challenge (its title, then its .challenge-body) and
   a column per task (--cols; js/rollouts.js sets it and puts the task names
   over the columns). A cell is a 4:3 still with its description as a caption
   over its foot, or a hatched empty slot; clicking a cell opens its clips in
   a .tcard-expand panel under its row. */
.challenges { container-type: inline-size; margin-top: 24px; }
.challenge-head, .challenge { display: grid; grid-template-columns: 160px minmax(0, 1fr); gap: 10px; }
.challenge-head { align-items: end; padding-bottom: 10px; }
.challenge { align-items: center; padding-block: 5px; scroll-margin-top: calc(var(--strip-h) + 20px); }
.challenge.is-apart { margin-top: 14px; padding-top: 18px; border-top: 2px dashed var(--ink-dim); }
.challenge-tasks, .challenge-body { display: grid; grid-template-columns: repeat(var(--cols, 5), minmax(0, 1fr)); gap: 10px; }
.challenge-tasks { align-items: end; }
.challenge-tasks > span { font-size: 14.5px; font-weight: 600; line-height: 1.35; text-align: center; color: var(--ink); text-wrap: balance; }
.challenge-title { display: flex; align-items: flex-start; gap: 8px; margin: 0; font-size: 14.5px; font-weight: 600; line-height: 1.35; color: var(--ink); text-wrap: balance; }
.challenge-title > .challenge-num { margin-top: -1px; }
.challenge-cell {
  position: relative; display: block; min-width: 0; padding: 0; overflow: hidden; text-align: left;
  border-radius: var(--radius); transition: box-shadow .15s;
}
.challenge-cell:hover { box-shadow: 0 0 0 2px var(--accent); }
/* While its clips play in the panel below: a thicker ring, set off by a gap */
.challenge-cell.is-open, .challenge-cell.is-open:hover { box-shadow: 0 0 0 2px var(--bg), 0 0 0 5px var(--accent); }
.challenge-still { position: relative; display: block; aspect-ratio: var(--clip-ar, 4 / 3); overflow: hidden; background: var(--video-bg); cursor: zoom-in; }
.challenge-still > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
.still-top .challenge-still > video { object-position: 50% 0; }
@media (prefers-reduced-motion: no-preference) {
  .challenge-still > video { transition: transform .35s ease; }
  .challenge-cell:hover .challenge-still > video { transform: scale(1.04); }
}
.challenge-still .play-badge { left: 8px; top: 8px; bottom: auto; width: 26px; height: 26px; }
.challenge-still .play-badge .play-tri { border-width: 4px 0 4px 7px; }
.challenge-cell:hover .play-badge { color: var(--accent); border-color: var(--accent); }
.challenge-cell.is-open .play-badge { display: none; }
/* The caption over the still's foot: a light fade behind the text only, and
   a dark halo around the letters to keep them readable, so the frame stays bright */
.challenge-cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  padding: 16px clamp(8px, .9cqi, 10px) 8px; color: var(--on-dark); text-shadow: 0 0 3px var(--scrim-shadow), 0 1px 2px var(--scrim-shadow);
  background: linear-gradient(to top, var(--scrim) calc(100% - 16px), transparent);
}
/* The task's name: shown over its column, so only read aloud here until the rows stack */
.challenge-task { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* Sized to the table (cqi: its width), so the caption fits the still as the
   cells narrow; --cap-size sets it for wider cells (.failure-clips) */
.challenge-desc { display: block; font-size: var(--cap-size, clamp(12px, 1.25cqi, 14px)); font-weight: 600; line-height: 1.3; }
.challenge-none {
  aspect-ratio: 4 / 3; border-radius: var(--radius); opacity: .5;
  background: repeating-linear-gradient(45deg, transparent 0 6px, var(--rule) 6px 7px);
}
.challenges > .tcard-expand { margin-block: 5px 12px; }
/* The visual-tactile policies' failures (.failure-clips, one grid per finding,
   in its .tactile-card): the same cells in a plain grid, three a row, with
   2:1 stills and clips (camera + tactile images), so larger captions; a
   cell's panel opens under its grid row */
.failure-clips { --clip-ar: 2 / 1; --cap-size: clamp(13px, 1.45cqi, 16px); container-type: inline-size; margin-top: 18px; }
.clip-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.failure-clips .challenge-desc { text-wrap: balance; }
/* A grid's data-tag (index.html), on each of its cells over the description:
   a light pill, clear of the caption's halo */
.clip-tag {
  display: inline-block; margin-bottom: 6px; padding: 2px 10px; border-radius: 999px;
  background: var(--tag-bg); color: var(--ink); text-shadow: none;
  font-size: 13.5px; font-weight: 600; line-height: 1.45;
}
/* A note in a grid (index.html): a tile the size of a still, outlined like an
   empty slot, holding a sentence, centred top to bottom (it grows if the
   sentence needs more room; the cells beside it keep their size) */
.clip-grid > .challenge-cell { align-self: start; }
.clip-note {
  display: block; align-content: center; aspect-ratio: var(--clip-ar, 4 / 3); min-width: 0; margin: 0;
  padding: 12px 14px; border: 1px dashed var(--ink-dim); border-radius: var(--radius);
  font-size: clamp(12.5px, 1.3cqi, 14px); line-height: 1.5; color: var(--ink-soft);
}
/* A narrow table: each row stacks, its title over its cells, and each cell
   becomes a card, its caption (now naming the task) under the still (the
   same for the cells of a narrow .failure-clips grid) */
@container (max-width: 780px) {
  .challenge-head, .challenge-none { display: none; }
  .challenge { grid-template-columns: minmax(0, 1fr); padding-block: 10px; }
  .challenge-body { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
  .clip-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
  .clip-grid > .challenge-cell { align-self: stretch; }  /* cards in a row, as tall as each other */
  .challenge-title { font-size: 15.5px; }
  .challenge-cell { display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--rule); }
  .challenge-cell:hover { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
  .challenge-cell.is-open { border-color: var(--accent); }
  .challenge-still { border-bottom: 1px solid var(--rule); }
  .challenge-cap { position: static; padding: 8px 10px 10px; background: none; color: var(--ink-soft); text-shadow: none; }
  .challenge-task {
    position: static; display: block; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal;
    margin-bottom: 2px; font-size: 13.5px; font-weight: 600; line-height: 1.35; color: var(--ink);
  }
  .challenge-desc { font-size: 12.5px; font-weight: 400; line-height: 1.4; }
  /* On the white card: tinted like the finding's number, so it stands out */
  .clip-tag { margin: 2px 0 4px; padding: 1px 8px; background: var(--tactile-wash); color: var(--tactile); font-size: 12.5px; }
}
/* The method and episode, in the top-left corner of a video in the panel
   (clips that show a force reading have it in the top-right) */
.clip-chip {
  position: absolute; left: 14px; top: 14px; z-index: 1; pointer-events: none;
  padding: 7px 18px; border-radius: 999px; background: var(--vidmeta-bg);
  font-size: 20px; font-weight: 500; line-height: 1.4; color: var(--ink); font-variant-numeric: tabular-nums;
}
.clip-m { color: var(--accent); font-weight: 600; }
/* The panel: the description under the title, and a frame (4:3, or --clip-ar) sized so the panel fits in the window */
.cx-text { flex: 1; min-width: 0; }
.cx-desc { margin-top: 2px; font-size: 14.5px; line-height: 1.55; color: var(--ink-soft); }
.clip-expand .player { width: 100%; max-width: calc(64vh * var(--clip-ar, 4 / 3)); margin: 0 auto 12px; gap: 6px; }
.clip-video {
  position: relative; aspect-ratio: var(--clip-ar, 4 / 3);
  background: var(--video-bg); border-radius: 4px; overflow: hidden;
}
.clip-video video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }

.rollouts {
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius); padding: 20px 24px 18px;
  display: flex; flex-direction: column; gap: 18px;
}
.ro-controls { display: flex; flex-direction: column; gap: 12px; }
.ro-row { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 12px; align-items: baseline; }
.ro-k { font-size: 14px; font-weight: 500; color: var(--ink-soft); }
.ro-opts { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.ro-opt {
  font-size: 13.5px; line-height: 1.3;
  padding: 5px 12px; border: 1px solid var(--rule); border-radius: 999px; color: var(--ink-soft); text-align: left;
  transition: background .15s, border-color .15s, color .15s;
}
.ro-opt:hover { color: var(--accent); border-color: var(--accent); }
.ro-opt[aria-checked="true"],
.ro-opt[aria-pressed="true"] { color: var(--accent); background: var(--chip-bg); border-color: var(--accent); font-weight: 500; }
/* Visual + behavioral video variants (task page) */
.ro-opt.vb[aria-checked="true"] { color: var(--warm); background: var(--vb-wash); border-color: var(--warm); }
/* Not run with the chosen method: filled grey with no outline, so it reads
   as unavailable rather than just unselected */
.ro-opt:disabled,
.ro-opt:disabled:hover { color: var(--ink-dim); background: var(--panel-2); border-color: transparent; cursor: not-allowed; }
.ro-note { font-size: 12.5px; color: var(--ink-dim); margin-left: 4px; }
/* Episode numbers: small square buttons in an even grid */
.ro-eps { display: grid; grid-template-columns: repeat(auto-fill, minmax(34px, 1fr)); gap: 4px; }
.ro-eps .ro-opt {
  padding: 5px 0; border-radius: 4px; text-align: center;
  font-size: 13px; font-variant-numeric: tabular-nums;
}

/* Every clip spans the full width: js/rollouts.js sets the frame's
   aspect-ratio to the clip's once it loads (this is the starting guess) */
.ro-video {
  position: relative; aspect-ratio: 1720 / 820;
  background: var(--video-bg); border-radius: 4px; overflow: hidden;
}
.ro-video video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.ro-err {
  position: absolute; left: 12px; right: 12px; bottom: 12px; margin: 0; pointer-events: none;
  padding: 8px 12px; background: var(--vidmeta-bg); border-left: 2px solid var(--warm);
  font-size: 13px; color: var(--ink);
}

/* Quantitative results figure (above the viewer), boxed like the carousel */
.results-fig {
  margin: 0; padding: 16px 24px 20px;
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
}
.results-fig img { display: block; width: 100%; height: auto; }
.results-fig figcaption { margin-bottom: 18px; text-align: center; font-size: 20px; font-weight: 600; line-height: 1.5; color: var(--ink); }
.results-fig + .results-fig { margin-top: 28px; }
/* The results tables sit straight on the page, without the white box */
.results-fig.rp-fig { background: none; border: 0; padding-inline: 0; padding-bottom: 0; }
.rp-fig .rtable thead tr:first-child th:first-child, .rp-fig .rtable tbody th { background: var(--bg); }

/* 3D-printable assets: a segmented printer control over the door handle and
   outlet clips side by side (js/main.js shows the chosen printer's set),
   straight on the page without the white box */
.results-fig.asset-fig { background: none; border: 0; padding: 0; }
/* The hint over the printer control */
.asset-hint { margin: 0 0 10px; text-align: center; font-size: 14px; line-height: 1.5; color: var(--ink-soft); }
/* Segmented control: the printer control */
.asset-printers {
  position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  width: max-content; margin: 0 auto 16px; padding: 3px;
  background: var(--panel); border: 1px solid var(--rule); border-radius: 999px;
}
/* The accent pill behind the chosen option; js/main.js sets --i to its index */
.asset-printers-pill {
  position: absolute; top: 3px; bottom: 3px; left: 3px; width: calc(50% - 3px);
  background: var(--accent); border-radius: 999px;
  transform: translateX(calc(var(--i, 0) * 100%));
}
.asset-printers button {
  position: relative; padding: 6px 18px; border-radius: 999px; white-space: nowrap;
  font-size: 14.5px; font-weight: 500; color: var(--ink-soft);
}
.asset-printers button:hover { color: var(--accent); }
.asset-printers button[aria-checked="true"] { color: var(--panel); }
@media (prefers-reduced-motion: no-preference) {
  .asset-printers-pill { transition: transform .22s ease; }
  .asset-printers button { transition: color .2s; }
}
.asset-set { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.asset-set[hidden] { display: none; }
.asset-clip { margin: 0; }
.asset-video {
  position: relative; aspect-ratio: 4 / 3;
  background: var(--video-bg); border-radius: 4px; overflow: hidden;
}
.asset-video video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.asset-clip figcaption { margin-top: 8px; text-align: center; font-size: 15px; font-weight: 500; color: var(--ink); }

/* Results tables: one metric per panel, stacked and slid like the demos
   carousel below (js/rollouts.js adds the buttons and dots). Each table
   scrolls sideways inside its panel when the window is too narrow. */
.rp { display: flex; flex-direction: column; gap: 14px; }
.rp-view { overflow: hidden; }
.rp-track { display: grid; }
.rp-panel { display: flex; flex-direction: column; gap: 10px; }
/* Each panel's title is the block's title, styled like .results-fig figcaption */
.rp-title { margin: 0 0 8px; text-align: center; font-size: 20px; font-weight: 600; line-height: 1.5; color: var(--ink); }
.rp-scroll { overflow-x: auto; }
/* A sentence on what the table shows, above the title */
.rp-desc { margin: 0 0 8px; font-size: 15.5px; line-height: 1.65; color: var(--ink); }
.rp-note { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--ink-soft); }
.rp-note sup, .rtable sup { line-height: 0; }

/* Booktabs look: heavy top and bottom rules, light rule under the header */
.rtable {
  width: 100%; border-collapse: collapse;
  font-size: 15px; line-height: 1.35; font-variant-numeric: tabular-nums; color: var(--ink);
  border-top: 1.5px solid var(--rule-strong); border-bottom: 1.5px solid var(--rule-strong);
}
.rtable th, .rtable td { padding: 9px 10px; text-align: center; white-space: nowrap; }
.rtable thead th { font-weight: 600; vertical-align: bottom; }
.rtable thead tr:last-child th { border-bottom: 1px solid var(--rule-strong); }
.rtable tbody tr + tr > * { border-top: 1px solid var(--rule); }
/* Task names: left-aligned, wrapping, and kept in view while the table scrolls */
.rtable thead tr:first-child th:first-child, .rtable tbody th {
  position: sticky; left: 0; z-index: 1; background: var(--panel);
  text-align: left; padding-left: 0;
}
.rtable tbody th { font-weight: 500; }
.rtable tbody th a { color: inherit; text-decoration: none; transition: color .15s; }
.rtable tbody th a:hover { color: var(--accent); }
/* A method's columns: a short rule under its name, a gap before its first column */
.rtable .rt-grp {
  padding-bottom: 6px;
  background: linear-gradient(var(--rule-strong), var(--rule-strong)) bottom / calc(100% - 16px) 1px no-repeat;
}
.rtable .g { padding-left: 22px; }
.rtable .ne { font-style: italic; color: var(--ink-dim); }
.rtable .rt-human { background: var(--panel-3); }
/* The column after Human demos: a wider gap before the methods */
.rtable .rt-sep { padding-left: 40px; }
.rt-dir { color: var(--ink-dim); font-weight: 400; }

/* Human demonstrations carousel (above the viewer): one task per panel, the
   video on the left and its name and description on the right. The panels
   are stacked in one grid cell (so the box fits the tallest); js/rollouts.js
   slides the new one in from the side and the old one out. */
.demos-fig {
  margin: 0; padding: 20px 24px 14px;
  background: var(--panel); border: 1px solid var(--rule); border-radius: var(--radius);
}
.demos { display: flex; flex-direction: column; gap: 14px; }
.demos-fig + .lead { margin-top: 28px; }
/* The caption sits right under each video, centred */
.demo-caption { margin: 6px 0 0; text-align: center; font-size: 14px; line-height: 1.5; color: var(--ink-soft); }
.demo-view { overflow: hidden; }
.demo-track { display: grid; }
.demo-slide, .rp-panel, .clip-slide {
  grid-area: 1 / 1; min-width: 0;
  transform: translateX(100%); visibility: hidden;
  transition: transform .45s cubic-bezier(.4, 0, .2, 1);
}
.demo-slide { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 22px 28px; align-items: center; align-content: start; }
.demo-slide.is-current, .rp-panel.is-current, .clip-slide.is-current { transform: none; visibility: visible; }
.demo-slide.is-leaving, .rp-panel.is-leaving, .clip-slide.is-leaving { visibility: visible; }
.demo-slide.no-anim, .rp-panel.no-anim, .clip-slide.no-anim { transition: none; }
/* One box shape for every demo, as wide as the widest video (camera +
   tactile strip, 2:1). Videos sit at its left, so the camera view keeps
   its size and place from panel to panel; a narrower video (camera only)
   leaves a strip on the right that shows the demo's tag. js/rollouts.js
   sets --vr (the video's width / height) and .is-narrow. */
.demo-video {
  --demo-ar: 2;
  position: relative; aspect-ratio: var(--demo-ar);
  background: var(--video-bg); border-radius: 4px; overflow: hidden;
}
.demo-video video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; object-position: left center; }
.demo-video-note {
  position: absolute; top: 0; bottom: 0; right: 0; left: calc(var(--vr, 2) / var(--demo-ar) * 100%);
  display: none; place-items: center; padding: 8px; margin: 0;
  font-size: 20px; font-weight: 500; line-height: 1.3; text-align: center; color: var(--ink-soft);
}
.demo-video.is-narrow video { width: calc(var(--vr) / var(--demo-ar) * 100%); }
.demo-video.is-narrow .demo-video-note { display: grid; }
/* Round play button in the middle of a paused video (a narrow one's own
   width, not the whole box); hidden while it plays */
.demo-play {
  position: absolute; left: 50%; top: 50%; z-index: 1; transform: translate(-50%, -50%);
  padding: 0; border: 0; border-radius: 50%; background: none; cursor: pointer;
}
.demo-video.is-narrow .demo-play { left: calc(var(--vr) / var(--demo-ar) * 50%); }
.demo-video.is-playing .demo-play { display: none; }
.demo-video video:not([controls]) { cursor: pointer; }
.demo-play .play-badge { position: static; width: 56px; height: 56px; padding-left: 4px; }
.demo-play .play-badge .play-tri { border-width: 10px 0 10px 16px; }
.demo-count {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; margin: 0 0 6px;
  font-size: 13.5px; font-weight: 500; color: var(--accent); font-variant-numeric: tabular-nums;
}
.demo-name { margin: 0 0 10px; font-size: 20px; font-weight: 600; line-height: 1.3; color: var(--ink); }
.demo-name a { color: inherit; text-decoration: none; transition: color .15s; }
.demo-name a:hover { color: var(--accent); }
.demo-arrow { margin-left: 8px; color: var(--accent); }
.demo-desc { font-size: 15.5px; line-height: 1.65; color: var(--ink-soft); }
.demo-lead { font-weight: 600; color: var(--ink); }
.demo-nav { display: flex; justify-content: space-between; align-items: center; gap: 12px; }

/* Task progress: the lower half of each demo panel (the paper's Fig. 9).
   A row per checkpoint and a column per method; each cell is a number and
   a bar in the method's colour (--m, which js/rollouts.js sets per cell
   from --m-<key>). The bars grow in as their panel slides in. */
.demo-progress {
  grid-column: 1 / -1; container-type: inline-size;
  padding-top: 18px; border-top: 1px solid var(--rule);
  scroll-margin-top: calc(var(--strip-h) + 24px);  /* a link to #task-progress lands here */
}
/* The heading over the table and the note under it, both centred */
.pg-title { margin: 0 0 8px; text-align: center; text-wrap: balance; font-size: 18px; font-weight: 600; line-height: 1.4; color: var(--ink); }
.pg-note { margin: 10px 0 0; text-align: center; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); }
.pg-table {
  width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0;
  font-size: 14.5px; line-height: 1.35; font-variant-numeric: tabular-nums; color: var(--ink);
}
.pg-table th, .pg-table td { padding: 5px 10px; }
.pg-table thead th { padding-bottom: 7px; border-bottom: 1px solid var(--rule); vertical-align: bottom; }
.pg-table tbody tr:first-child > * { padding-top: 9px; }
.pg-table .pg-corner { width: 34%; padding-left: 0; text-align: left; font-size: 13px; font-weight: 400; font-style: italic; color: var(--ink-dim); }
.pg-m { text-align: center; }
.pg-abbr { display: block; white-space: nowrap; font-weight: 600; }
.pg-input { display: block; white-space: nowrap; font-size: 12.5px; font-weight: 400; font-style: italic; color: var(--ink-dim); }
/* Checkpoint names, numbered in order, wrapped lines indented past the number */
.pg-table tbody th { position: relative; padding-left: 22px; text-align: left; font-weight: 400; }
.pg-step { position: absolute; left: 0; color: var(--ink-dim); }
.pg-table tbody tr:hover > :not(.pg-ne) { background: var(--panel-3); }
.pg-cell { display: flex; align-items: center; gap: 8px; }
.pg-val { flex: none; width: 3ch; text-align: right; }
/* The bar: a track the full cell wide, filled to the share (--v, 0–1) from a square start */
.pg-bar { flex: 1; height: 10px; border-radius: 0 3px 3px 0; background: var(--panel-2); }
.pg-bar::before {
  content: ""; display: block; height: 100%; width: calc(var(--v) * 100%);
  border-radius: inherit; background: var(--m, var(--ink-dim));
  transition: width .6s cubic-bezier(.4, 0, .2, 1) .2s;
}
.demo-slide:not(.is-current):not(.is-leaving) .pg-bar::before { width: 0; transition: none; }
.pg-ne {
  vertical-align: middle; text-align: center;
  font-size: 13.5px; font-style: italic; color: var(--ink-dim);
  background: var(--preview-pattern); background-clip: content-box;
}
/* A narrow panel: each number sits over its bar */
@container (max-width: 560px) {
  .pg-table { font-size: 13.5px; }
  .pg-table th, .pg-table td { padding-inline: 4px; }
  .pg-table .pg-corner { width: 30%; font-size: 12.5px; }
  .pg-m { text-align: left; }
  .pg-input { font-size: 11.5px; }
  .pg-table tbody th { padding-left: 16px; }
  .pg-cell { flex-direction: column; align-items: stretch; gap: 3px; }
  .pg-val { width: auto; text-align: left; }
  .pg-bar { flex: none; height: 6px; }
  /* Too narrow for the words side by side: run them up the column */
  .pg-ne > span { display: inline-block; writing-mode: vertical-rl; transform: rotate(180deg); }
}
@container (max-width: 290px) {
  .pg-table th, .pg-table td { padding-inline: 2px; }
  .pg-table .pg-corner { width: 28%; }
  .pg-abbr { font-size: 12.5px; }
  .pg-input { font-size: 10.5px; }
}

.ro-stage { display: flex; flex-direction: column; gap: 12px; }
.ro-bar { display: flex; justify-content: space-between; align-items: center; gap: 8px 16px; flex-wrap: wrap; }
.ro-now { margin: 0; display: flex; flex-wrap: wrap; gap: 2px 0; font-size: 14.5px; color: var(--ink-soft); }
.ro-now > span:not(:last-child)::after { content: "·"; margin: 0 8px; color: var(--ink-dim); }
.ro-now-m { color: var(--accent); font-weight: 500; }
.ro-now-e { font-variant-numeric: tabular-nums; }
.ro-step { display: flex; gap: 6px; }
.ro-btn {
  padding: 5px 12px; border: 1px solid var(--rule); border-radius: 4px; background: var(--panel);
  font-size: 13.5px; font-weight: 500; color: var(--ink);
  transition: color .15s, border-color .15s;
}
.ro-btn:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.ro-btn:disabled { color: var(--ink-dim); cursor: default; }

/* ---------- Config problems banner (only shown when a config is off) ---------- */

.config-errors {
  margin: 16px 24px 0; padding: 14px 18px;
  background: var(--panel); border: 1px solid var(--warm); border-left-width: 3px;
  font-family: var(--mono); font-size: 12px; line-height: 1.6; color: var(--ink);
}
.config-errors strong { color: var(--warm); font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; font-size: 11px; }
.config-errors ul { margin: 6px 0 0; padding-left: 18px; }

/* ---------- Toast (placeholder links, copy feedback) ---------- */

.toast {
  position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom, 0px) + 20px); z-index: 60;
  transform: translateX(-50%); max-width: calc(100vw - 32px);
  padding: 10px 16px; background: var(--ink); color: var(--panel); border-radius: var(--radius);
  font-size: 14px;
  animation: toast-in .2s ease-out;
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 6px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .skill-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .card-grid.lh-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  /* Simulation: the list becomes a swipeable row of stills over the player */
  .sim-player { grid-template-columns: minmax(0, 1fr); }
  .sim-list-wrap { position: static; }
  .sim-list { position: relative; inset: auto; flex-direction: row; gap: 6px; overflow-x: auto; overflow-y: hidden; padding-bottom: 4px; }
  .sim-list > li { flex: 0 0 148px; }
  .sim-item { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  /* Asset variations: each band's text and stills over its player */
  .var-band { grid-template-columns: minmax(0, 1fr); gap: 20px; }
}
@media (max-width: 960px) {
  .layout > .toc, .layout > .content { grid-area: auto; }
  .content { width: auto; margin-left: 0; }
  /* The contents bar becomes a sticky, swipeable strip */
  .toc {
    display: flex; align-items: center; gap: 6px;
    width: auto; justify-self: stretch;
    top: env(safe-area-inset-top, 0px); z-index: 30; max-height: none;
    overflow-x: auto; overflow-y: hidden;
    margin: 0 -24px 32px; padding: 10px 24px;
    background: var(--bg); border-bottom: 1px solid var(--rule);
  }
  .toc-label { display: none; }
  .toc-top { display: flex; gap: 6px; }
  .toc li, .toc-sub, .toc-row, .toc-sub-wrap { display: contents; }
  .has-sub.is-collapsed .toc-sub-wrap { display: none; }
  .toc a {
    display: inline-flex; align-items: baseline; gap: 8px; white-space: nowrap;
    padding: 6px 12px; border: 1px solid var(--rule); border-radius: 999px;
  }
  .toc a.is-active, .toc .has-sub.is-collapsed a.is-parent { border-color: var(--accent); }
  /* The arrow is its own pill after the section; open, it points back */
  .toc-toggle {
    position: static; transform: none; flex-shrink: 0;
    width: 32px; height: 32px; border: 1px solid var(--rule); border-radius: 999px;
  }
  .toc-toggle svg { transform: rotate(180deg); }
  .toc-backs { display: contents; }
  .toc a.toc-back { flex-shrink: 0; }
}
@media (max-width: 640px) {
  .page { padding-inline: 16px; }
  .toc { margin-inline: -16px; padding-inline: 16px; }
  .cover { padding-block: 40px 32px; padding-inline: 16px; }
  .teaser { margin-top: 32px; }
  /* Two cards a row */
  .card-grid, .card-grid.is-wide { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
  .tcard-cap { padding: 9px 10px 9px 12px; gap: 6px; }
  .tcard-name { font-size: 14px; }
  .tcard-arrow { font-size: 16px; }
  .tcard-link { padding: 4px 10px; font-size: 13px; }
  .filters { padding: 14px; }
  .tcard-expand { padding: 12px 12px 8px; }
  .skill-tiles { gap: 10px; }
  .skill-tile-body { padding: 10px; }
  .skill-tile-name { font-size: 14.5px; }
  .lh-band { margin-top: 28px; }
  .sim-player { padding: 12px; gap: 12px; }
  .sim-list > li { flex-basis: 128px; }
  .var-band { padding: 14px; gap: 14px; }
  .var-title { font-size: 17px; }
  .tx-title { font-size: 15px; }
  .task-expand .tx-title { font-size: 18px; }
  .tx-details { padding: 6px 12px; gap: 6px; font-size: 13.5px; }
  .scroll-bar { padding-inline: 16px; }
  .lightbox { width: min(1280px, calc(100vw - 24px), calc((100vh - 150px) * 16 / 9)); }
  .print-part { grid-template-columns: 36px minmax(0, 1fr); gap: 4px 10px; }
  .print-part-img { width: 36px; height: 27px; }
  .print-part-files { grid-column: 2; }
  .print-body { padding: 12px; }
  .vnav { width: 34px; height: 34px; font-size: 21px; }
  .vnav.prev { left: 8px; }
  .vnav.next { right: 8px; }
  .vgrid { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); padding: 10px; }
  .spec { grid-template-columns: 1fr; gap: 0; }
  .spec .rk { margin-bottom: 4px; }
  .spec .rv:not(:last-child) { margin-bottom: 16px; }
  .crit-spec { grid-template-columns: 1fr; }
  .crit-spec .rk { border-right: 0; border-bottom: 0; padding-bottom: 4px; }
  .crit-spec .rv { padding-top: 4px; }
  .rollouts { padding: 16px 16px 14px; gap: 14px; }
  .demos-fig, .results-fig { padding: 16px 16px 12px; }
  .asset-set { gap: 10px; }
  .asset-clip figcaption { font-size: 14px; }
  .rtable { font-size: 14px; }
  .rtable th, .rtable td { padding-inline: 8px; }
  .rtable thead tr:first-child th:first-child, .rtable tbody th { min-width: 124px; white-space: normal; }
  .rtable .g { padding-left: 16px; }
  .rtable .rt-sep { padding-left: 28px; }
  .demos { gap: 10px; }
  .demo-slide { grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start; }
  .demo-progress { padding-top: 14px; }
  .demo-name { font-size: 18px; margin-bottom: 6px; }
  .demo-video-note { font-size: 16px; }
  .demo-play .play-badge { width: 46px; height: 46px; padding-left: 3px; }
  .demo-play .play-badge .play-tri { border-width: 8px 0 8px 13px; }
  .clip-chip { left: 8px; top: 8px; padding: 5px 12px; font-size: 15px; }
  /* The player comes first and stays in view below the contents strip
     while the reader works through the choices under it */
  .ro-stage {
    order: -1; position: sticky; top: var(--strip-h); z-index: 5;
    margin: -16px -16px 0; padding: 12px 16px; gap: 10px;
    background: var(--panel); border-bottom: 1px solid var(--rule); border-radius: var(--radius) var(--radius) 0 0;
  }
  .ro-bar { flex-wrap: nowrap; }
  .ro-now { font-size: 13.5px; min-width: 0; }
  .ro-btn { padding-inline: 11px; font-size: 16px; line-height: 1.2; }
  .ro-btn-t { display: none; }
  .ro-row { grid-template-columns: 1fr; gap: 6px; }
  .ro-eps { grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); }
  .ro-eps .ro-opt { padding-block: 7px; }
}

/* ---------- Motion preferences ---------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
