/* Style tokens follow gauravparmar.com: off-white page, near-black text,
   Newsreader for prose, JetBrains Mono for labels and controls. */
:root {
  --bg: #FCFCFC;
  --fg: #1A1A1A;
  --muted: #737373;
  --faint: #A3A3A3;
  --rule: #E5E5E5;
  --btn-border: #D4D4D4;
  --placeholder: #F2F2F2;
  --key: #2F8F83; /* 03 Method figure: input patches (keys) */
  --val: #D9652B; /* 03 Method figure: output patches (values) */
  --agent-fg: #2B4C9B;
  --agent-bg: #EEF2FA;
  --agent-bg-hover: #E1E8F6;
  --serif: "Newsreader", Georgia, serif;
  --mono: "JetBrains Mono", ui-monospace, monospace;
  --col: 720px;
  --wide: 1100px;
  --gutter: 16px;
}

* { box-sizing: border-box; }

html {
  overflow-anchor: none; /* script.js keeps the reading position itself */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 16px/24px var(--serif);
}

a { color: inherit; }

.wrap,
.wide {
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.wrap { max-width: calc(var(--col) + 2 * var(--gutter)); }
.wide { max-width: calc(var(--wide) + 2 * var(--gutter)); }

/* ---------- Header and level toggle ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  padding: 16px 0 14px;
  border-bottom: 1px solid var(--fg);
}
.brand {
  font: 700 14px/20px var(--mono);
  letter-spacing: 0.08em;
  text-decoration: none;
}
/* Segmented control: two boxed versions (selected one filled black), a "Deep dive" link to
   the paper on arXiv, plus a blue "Agents" link to llms.txt. */
.levels {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--fg);
}
.levels button,
.levels .out,
.levels .agents {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 128px;
  padding: 7px 14px 8px;
  font: inherit;
  text-align: left;
  color: var(--fg);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: none;
}
.levels > * + * { border-left: 1px solid var(--fg); }
.levels b {
  font: 500 12px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.levels small {
  font: 10px/14px var(--mono);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.levels button:hover,
.levels .out:hover { background: var(--placeholder); }
.levels button[aria-pressed="true"] { color: var(--bg); background: var(--fg); }
.levels button[aria-pressed="true"] small { color: var(--faint); }
.levels .soon,
.levels .soon:hover { color: var(--faint); background: none; cursor: default; } /* a tile that is not a link yet */
.levels .soon small { color: var(--faint); }
.levels .agents { color: var(--agent-fg); background: var(--agent-bg); }
.levels .agents small { color: var(--agent-fg); opacity: 0.75; }
.levels .agents:hover { background: var(--agent-bg-hover); }

/* ---------- Landing cover ----------
   A fixed full-screen layer: title block above, the four tiles below, both centered.
   It shows at level 0, and during the move between the cover and a version (.cover-on).
   The auto margins center the content but still let it scroll on very short screens. */
.cover {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: none;
  flex-direction: column;
  align-items: center;
  padding: 32px var(--gutter);
  overflow-y: auto;
  background: var(--bg);
  text-align: center;
}
body[data-level="0"] .cover,
body.cover-on .cover { display: flex; }
.cover-title { max-width: var(--col); margin-top: auto; }
.cover-tiles {
  display: flex;
  justify-content: center;
  width: 100%;
  max-width: var(--col);
  margin: 64px 0 auto;
}
.cover .buttons { justify-content: center; margin-bottom: 0; }

/* Landing results strip. */
.cover-media { width: 100%; max-width: var(--col); margin-top: 48px; }

/* A box that holds one of the shared gazebo videos (script.js moves the videos between
   boxes). The box keeps its size when empty, so the layout does not jump. */
.vslot {
  display: block;
  aspect-ratio: 832 / 480;
  background: var(--placeholder);
}
.vslot video { display: block; width: 100%; height: 100%; object-fit: cover; }

/* Input box: the RGB video fills it; its depth video is a small inset in the
   bottom-right corner with a thin white border. Sizes are in % so it scales with the box. */
.input-pair { position: relative; width: 100%; height: 100%; }
.input-pair .depth {
  position: absolute;
  right: 4%;
  bottom: 6%;
  width: 34%;
  height: auto;
  aspect-ratio: 832 / 480;
  border: 1px solid #fff;
}

.media-label {
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* [input] -> [output] [output], labels under the input and under the outputs. */
.cover-media.strip {
  display: grid;
  grid-template-columns: 1fr 24px 1fr 1fr;
  gap: 8px 10px;
  align-items: center;
  cursor: pointer;
}
.strip-arrow { font: 14px/1 var(--mono); color: var(--muted); }
.strip .media-label { grid-row: 2; align-self: start; }
.label-in { grid-column: 1; }
.label-out { grid-column: 3 / span 2; }

/* The fixed copy of the tiles that flies between the cover and the header. */
.levels.flying { position: fixed; z-index: 30; margin: 0; }

/* ---------- Title block ---------- */
.venue {
  display: inline-block;
  margin-bottom: 14px;
  padding: 2px 7px;
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  color: var(--bg);
  background: var(--fg);
}
.cover h1 {
  font: 500 40px/1.15 var(--serif);
  letter-spacing: -0.015em;
  margin: 0 0 20px;
}
.authors,
.affil {
  font: 12px/20px var(--mono);
  margin: 0;
}
.authors { color: var(--muted); }
.authors a {
  text-decoration-color: var(--btn-border);
  text-underline-offset: 3px;
}
.authors a:hover { color: var(--fg); text-decoration-color: var(--fg); }
.affil { color: var(--faint); margin-top: 4px; }
.buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 20px 0 32px;
}
.btn {
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: 1px solid var(--btn-border);
  padding: 5px 9px;
}
a.btn { text-decoration: none; }
a.btn:hover { border-color: var(--fg); }

/* ---------- TL;DR: one sentence, then the results ---------- */
.tldr { padding-top: 56px; }
.lead {
  margin: 0 0 40px;
  font: 400 32px/42px var(--serif);
  letter-spacing: -0.015em;
  text-align: center;
}
.lead > span { display: block; } /* one phrase per line */

/* The lead comes in phrase by phrase when the reader leaves the cover (script.js adds .play).
   The results come in after the second phrase. */
.tldr.play .lead > span,
.tldr.play .results { animation: reveal 0.6s ease both; }
.tldr.play .lead > span:nth-child(1) { animation-delay: 0.3s; }
.tldr.play .lead > span:nth-child(2) { animation-delay: 0.8s; }
.tldr.play .results { animation-delay: 1.3s; }
.tldr.play .lead > span:nth-child(3) { animation-delay: 1.8s; }

/* The key words: fast, causal, long durations. */
.lead em {
  font-style: normal;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.3em; /* below the descenders */
}

/* [input] -> 2 x 2 outputs, labels below. The input box is as tall as the two output rows. */
.results {
  display: grid;
  grid-template: "in arrow out" "lin . lout" / minmax(0, 1fr) 24px minmax(0, 1fr); /* two equal halves */
  gap: 8px 10px;
  cursor: pointer;
}
.results > .vslot { grid-area: in; aspect-ratio: auto; }
.results .strip-arrow { grid-area: arrow; place-self: center; }
.outputs { grid-area: out; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.results .label-in { grid-area: lin; }
.results .label-out { grid-area: lout; display: flex; justify-content: space-between; }

/* ---------- Overview 01 and 02: a video plays along a filmstrip; the revisit is marked ---------- */

/* The headline states the insight; the figure under it shows it. */
.headline { margin: 0; font: 400 24px/32px var(--serif); letter-spacing: -0.01em; }
/* Citations: [1, 2, 3] in a sentence, muted; the footnotes at the end of the section give the names. */
.headline .cite,
.caption .cite { font-size: 0.75em; color: var(--muted); white-space: nowrap; }
.cite a { text-decoration: none; }
.cite a:hover,
.refs a:hover { color: var(--fg); }
.refs {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px; /* the three footnotes fit on one line of the page column */
  font: 11px/16px var(--mono);
  color: var(--muted);
}
.refs a { text-decoration: underline; text-decoration-color: var(--btn-border); text-underline-offset: 3px; }

/* Vertical rhythm, top to bottom (symmetric around the two crop rows):
   input label | input strip | --edge | input crops | --mid | output crops | --edge | output strip | output label
   --edge is measured from the frame edge, so the input time labels sit inside it. */
.reel { --n: 5; --gap: 12px; --crop: 72px; --edge: 44px; --mid: 28px; --sx: 368; --sy: 64; --ss: 112; position: relative; margin: 40px 0 0; }
.section .wrap > .reel { margin-top: 40px; } /* not the 24 px between other blocks of a section */
.strip-label {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 10px;
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.strip-wrap + .strip-label { margin: 10px 0 0; } /* the output label sits under the output strip */
.playing { color: var(--fg); opacity: 0; } /* script.js shows it while a strip plays */
.strip-wrap { position: relative; }
.reel .strip { display: grid; grid-template-columns: repeat(var(--n), 1fr); gap: var(--gap); }
.reel .strip figure { position: relative; margin: 0; }
/* All frames of a strip sit in the background: faded and mostly gray. Only the roof spot of
   the two revisit frames is in full color (the .box below). */
.reel .strip img {
  display: block;
  width: 100%;
  aspect-ratio: 640 / 369;
  opacity: 0.3;
  filter: grayscale(0.8);
}
.reel .strip figcaption {
  position: absolute;
  top: 100%;
  margin-top: 6px;
  font: 10px/14px var(--mono);
  color: var(--faint);
}
/* The spot that the figure compares: the square of size --ss at (--sx, --sy) of a 640 x 369
   frame (default: the roof spot of the gazebo). The box shows its part of the frame in full
   color (--img is the frame); the rest of the frame stays faded. */
.reel .box {
  position: absolute;
  left: calc(var(--sx) / 640 * 100%);
  top: calc(var(--sy) / 369 * 100%);
  width: calc(var(--ss) / 640 * 100%);
  height: calc(var(--ss) / 369 * 100%);
  border: 1px solid var(--fg);
  background: var(--img) no-repeat border-box calc(var(--sx) / (640 - var(--ss)) * 100%) calc(var(--sy) / (369 - var(--ss)) * 100%) / calc(640 / var(--ss) * 100%) calc(369 / var(--ss) * 100%);
}
/* The live player: a real video that glides along the strip while it plays (script.js places it). */
.live {
  position: absolute;
  z-index: 2;
  left: 0;
  top: 0;
  aspect-ratio: 640 / 369;
  outline: 2px solid var(--fg);
  outline-offset: 2px;
  background: var(--placeholder);
  opacity: 0;
  pointer-events: none;
}
.live video { display: block; width: 100%; height: 100%; object-fit: cover; }

/* 01 Problem: the context window of the model. A bracket above the strip that moves with the
   live video (script.js sets its position and width). */
.has-ctx { margin-top: 38px; } /* room for the bracket and its label */
.ctx {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  height: 7px;
  border: 1px solid var(--fg);
  border-bottom: 0;
  pointer-events: none;
}
.ctx b {
  position: absolute;
  left: 50%;
  bottom: 100%;
  margin-bottom: 3px;
  transform: translateX(-50%);
  font: 400 10px/14px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* While the sequence runs (.animate), everything after the input label starts hidden;
   script.js reveals each part in turn. Without .animate (no script, reduced motion, or after
   a re-layout at the end) the full final figure shows. */
.reel.animate .strip figure,
.reel.animate .box,
.reel.animate .ctx,
.reel.animate .zoom,
.reel.animate .tie,
.reel.animate .imply,
.reel.animate [data-part="out"],
.reel.animate .replay { opacity: 0; }
.reel.animate .links path { stroke-dashoffset: 1; }

/* Crops between the strips. Each crop sits in the column of its frame, centered under the
   frame's box, so the two lines to it are symmetric. It is --crop px wide; the background maps
   the source square (--sx, --sy, --ss) onto it. */
.zrow { display: grid; grid-template-columns: repeat(var(--n), 1fr); column-gap: var(--gap); }
.zrow[data-zrow="in"],
.zrow[data-zrow="base"] { margin-top: var(--edge); }
.zrow[data-zrow="out"] { margin: var(--mid) 0 var(--edge); }
.zoom { margin: 0 0 0 calc((var(--sx) + var(--ss) / 2) / 640 * 100% - var(--crop) / 2); justify-self: start; }
.zoom[data-k="a"] { grid-column: 1; }
.zoom[data-k="b"] { grid-column: -2; }
.zimg {
  display: block;
  width: var(--crop);
  height: var(--crop);
  background: var(--img) no-repeat calc(var(--sx) / var(--ss) * var(--crop) * -1) calc(var(--sy) / var(--ss) * var(--crop) * -1) / calc(640 / var(--ss) * var(--crop)) auto;
  border: 1px solid var(--fg);
}
/* "imply": a vertical arrow from the label of one crop row down to the label of the next
   (script.js sets its left, top and height). The two labels and this arrow read as one
   statement: matching input regions imply matching output regions. */
.imply { position: absolute; width: 0; border-left: 1px solid var(--fg); }
.imply::after { /* arrow head */
  content: "";
  position: absolute;
  left: -4px;
  bottom: 1px;
  width: 6px;
  height: 6px;
  border: solid var(--fg);
  border-width: 0 1px 1px 0;
  transform: rotate(45deg);
}
.imply b {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translate(-50%, -50%);
  padding: 1px 8px;
  font: 400 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--bg);
}
/* The tie between two crops: script.js sets its left, top and width. */
.tie { position: absolute; height: 0; border-top: 1px solid var(--fg); }
.tie b {
  position: absolute;
  left: 50%;
  top: 0;
  transform: translate(-50%, -50%);
  padding: 2px 12px;
  font: 400 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  background: var(--bg);
}
.replay {
  display: block;
  margin: 20px auto 0;
  padding: 4px 10px;
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 1px solid var(--btn-border);
  cursor: pointer;
}
.replay:hover { color: var(--fg); border-color: var(--fg); }

/* Lines from the corners of each box to the corners of its crop (drawn by script.js). */
.links {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  fill: none;
  stroke: var(--fg);
  stroke-width: 1;
}
.links path { stroke-dasharray: 1; } /* with pathLength="1": the line draws on as the offset goes 1 -> 0 */

/* ---------- 03 Method: the story scene ----------
   One inline SVG. The reader goes through the steps with Back / Next. For step N, script.js
   puts from-1 ... from-N and at-N on the figure. A part with class fK shows from step K on;
   --d delays it, so the parts of one step come in one after the other. Parts that move (the
   input frame and the patches) are drawn at the origin and placed with a CSS transform. */
.story { --ease: cubic-bezier(0.4, 0, 0.2, 1); margin: 0; }
.section .wrap > .story { margin-top: 32px; }
.story-scroll { overflow-x: auto; }
.story svg {
  display: block;
  width: 100%;
  min-width: 640px; /* a narrow screen scrolls the scene sideways; the text stays readable */
  height: auto;
  font: 8.5px/1 var(--mono);
  letter-spacing: 0.04em;
  text-anchor: middle;
}
.story text { fill: var(--muted); }
.story .start { text-anchor: start; }
.story .end { text-anchor: end; }
.story .t-title { font-size: 10px; letter-spacing: 0.06em; }
.story .c-fg { fill: var(--fg); }
.story .c-k { fill: var(--key); }
.story .c-v { fill: var(--val); }
.story .ln { fill: none; stroke: var(--fg); stroke-width: 1; }
.story .dash { stroke-dasharray: 3 3; }
.story .faint { stroke: var(--faint); }
.story .k { stroke: var(--key); } /* an input patch (a key) */
.story .v { stroke: var(--val); } /* an output patch (a value) */
.story .dot { fill: var(--fg); }
.story .bg { fill: var(--bg); }
.story .can { fill: var(--placeholder); stroke: var(--fg); stroke-width: 1.25; } /* the history bank: a cylinder */
.story .lid { fill: var(--bg); }
/* Parts that come in at a step. */
.story .f2,
.story .f3,
.story .f4,
.story .f5,
.story .f6,
.story .f7 { opacity: 0; transition: opacity 0.5s ease; }
.story.from-2 .f2,
.story.from-3 .f3,
.story.from-4 .f4,
.story.from-5 .f5,
.story.from-6 .f6,
.story.from-7 .f7 { opacity: 1; transition-delay: calc(var(--d, 0) * 0.3s); } /* the delay is only for the way in */
/* Step 1: the input frame is large and near the center. From step 2 on it is at the left. */
.story .in { transform: translate(0, 76px); transition: transform 0.8s var(--ease), opacity 0.8s ease; }
.story.at-1 .in { transform: translate(60px, 90px) scale(1.5); }
/* The depth frame: next to the input at step 1, below it from step 2 on. */
.story .dp { transform: translate(0, 176px); transition: transform 0.8s var(--ease), opacity 0.8s ease; }
.story.at-1 .dp { transform: translate(265px, 90px) scale(1.5); }
/* Step 3: patchify and flatten. Each tile starts on its place in the input frame (--c, --r).
   The frame breaks apart (the tiles spread), and then the tiles go into one column of small
   tokens (--i). The frame itself stays, faded. */
.story .in image { transition: opacity 0.5s ease; }
.story.from-3 .in image { opacity: 0.3; transition-delay: 0.3s; }
.story .tile { opacity: 0; transform: translate(calc(2.5px + var(--c) * 25px), calc(76px + var(--r) * 25px)); transition: opacity 0.5s ease; }
.story.from-3 .tile {
  opacity: 1;
  transform: translate(146px, calc(49px + var(--i) * 13px)) scale(0.48);
  animation: flatten 1.9s var(--ease) calc(0.3s + var(--i) * 0.03s) backwards;
}
/* Step 4: one token is the query; the other tokens step back. */
.story.from-4 .tile { opacity: 0.3; transition-delay: 0.6s; }
@keyframes flatten {
  0% { transform: translate(calc(2.5px + var(--c) * 25px), calc(76px + var(--r) * 25px)); }
  20%, 50% { transform: translate(calc(2.5px + var(--c) * 28px), calc(76px + var(--r) * 28px)); }
}
/* Before the reader reaches the scene (.wait, set by script.js), the frame and the text of
   step 1 are hidden; then they fade in, the frame first. */
.story.wait .in,
.story.wait .dp,
.story.wait .story-notes p.on { opacity: 0; }
.story.at-1 .story-notes p.on { transition-delay: 0.5s; }
/* Patches that fly: from their cell in a frame (small) to their place in the scene. */
.story .q,
.story .mk,
.story .r { opacity: 0; transition: transform 0.9s var(--ease) calc(var(--d, 0) * 0.3s), opacity 0.2s ease calc(var(--d, 0) * 0.3s); }
.story .q { --d: 2; transform: translate(146px, 140px) scale(0.5); } /* step 4: the query token grows in its place in the column */ /* step 3: the query, out of the input frame */
.story.from-4 .q { transform: translate(140px, 134px); opacity: 1; }
/* Step 4: a copy of the matching pair comes out of the bank: its key (.mk) and its value (.r). */
.story .mk { --d: 7; transform: translate(200px, 134px); }
.story.from-4 .mk { transform: translate(370px, 99.5px) scale(1.167); opacity: 1; } /* 28 px */
.story.from-5 .mk { --d: 0; opacity: 0.3; } /* step 5 is about the value */
.story .r { --d: 7; transform: translate(232px, 134px); }
.story.from-4 .r { transform: translate(404px, 99.5px) scale(1.167); opacity: 1; }
/* A label that shows only at step 4. */
.story .o4 { opacity: 0; transition: opacity 0.4s ease; }
.story.at-4 .o4 { opacity: 1; transition-delay: 2.9s; }
/* Step 7: the new pairs of this block. Each patch goes from its start (--s) to its place next
   to the bank (--m). A pair that is already stored stays there and fades (.skip). A pair
   that is new waits there, and then goes into its slot in the bank (--e) (.store). */
.story .cand { opacity: 0; }
.story.from-7 .skip {
  opacity: 0.3;
  transform: translate(var(--mx), var(--my));
  animation: cand-skip 3s var(--ease) 0.5s backwards;
}
.story.from-7 .store {
  opacity: 1;
  transform: translate(var(--ex), var(--ey));
  animation: cand-store 3.3s var(--ease) 0.5s backwards;
}
@keyframes cand-skip {
  0% { opacity: 0; transform: translate(var(--sx), var(--sy)) scale(var(--ss)); }
  6% { opacity: 1; }
  32%, 62% { opacity: 1; transform: translate(var(--mx), var(--my)); }
}
@keyframes cand-store {
  0% { opacity: 0; transform: translate(var(--sx), var(--sy)) scale(var(--ss)); }
  6% { opacity: 1; }
  29%, 72% { transform: translate(var(--mx), var(--my)); }
}
/* A pair in the bank is faded until it is the match of the query (step 4). */
.story .pair { opacity: 0.4; transition: opacity 0.5s ease; }
.story.from-4 .pair.hit { opacity: 1; transition-delay: 1.5s; }
/* Back / Next, below the scene. */
.story-nav { display: flex; align-items: center; margin-top: 16px; }
/* The footnote of the scene: below the buttons, from step 4 on. Its line is always there, so
   the page does not move when it comes in. */
.story-fn { margin: 12px 0 0; font-size: 13px; line-height: 18px; color: var(--muted); opacity: 0; transition: opacity 0.4s ease; }
.story.from-4 .story-fn { opacity: 1; }
.story-label { margin-right: auto; font: 11px/16px var(--mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.story-nav button {
  padding: 6px 12px;
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg);
  background: none;
  border: 1px solid var(--fg);
  cursor: pointer;
}
.story-nav button + button { margin-left: -1px; }
.story-nav button:hover { color: var(--bg); background: var(--fg); }
.story-nav button:disabled { color: var(--faint); background: none; cursor: default; }
/* The text of the step sits on the scene, next to the part that it is about. --x, --y, --w
   are in the units of the scene (720 x 286); the font size follows the width of the scene. */
.story-view { position: relative; container-type: inline-size; }
.story-notes { position: absolute; inset: 0; pointer-events: none; }
.story-notes p {
  position: absolute;
  left: calc(var(--x) / 720 * 100%);
  top: calc(var(--y) / 286 * 100%);
  width: calc(var(--w) / 720 * 100%);
  margin: 0;
  font-size: 2.1cqw; /* 15 px in a 720 px scene */
  line-height: 1.33;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.4s ease;
}
.story-notes p.on { visibility: visible; opacity: 1; }
.story-notes u { text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
.story-notes sup { font-size: 0.7em; line-height: 0; }

/* A paragraph of text in a section: the caption above a figure. */
.caption { margin: 0; font-size: 17px; line-height: 26px; }

/* ---------- 04 Results: the LPIPS chart ----------
   A bar chart in plain HTML and CSS, one panel for each benchmark. Each pair of bars has two
   values: --a (baseline) and --b (with correspondence). A bar is value / --max of the plot
   height. script.js sets .wait until the reader reaches the chart; then the bars grow. */
.chart { --max: 0.7; --bar: 24px; --gap: 4px; margin: 0; }
.chart-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 4px 24px; margin-bottom: 16px; }
.chart-title { margin: 0; font-size: 16px; line-height: 24px; }
.chart-title span,
.chart-legend,
.chart-group,
.chart .tick,
.chart .pct { font: 11px/16px var(--mono); letter-spacing: 0.04em; }
.chart-title span { margin-left: 8px; color: var(--muted); text-transform: uppercase; }
.chart-legend { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 16px; width: 100%; margin: 4px 0 0; color: var(--muted); text-transform: uppercase; } /* its own line, in the center */
.chart-legend span { display: flex; align-items: center; gap: 6px; }
.chart-legend .bar { width: 12px; height: 12px; }
.chart-panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 40px; }
.chart-group { margin: 0 0 8px 28px; color: var(--muted); text-transform: uppercase; text-align: center; }
/* The plot: hairlines at 0, 0.2, 0.4 and 0.6; one column for each pair of bars. */
.chart-plot {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  height: 200px;
  margin-left: 28px; /* the y-axis labels */
  background: linear-gradient(to top, var(--rule) 1px, transparent 1px) 0 100% / 100% calc(100% * 0.2 / var(--max));
}
.chart .tick { position: absolute; right: 100%; bottom: calc(var(--v) / var(--max) * 100%); margin-right: 8px; transform: translateY(50%); color: var(--faint); }
.pair { position: relative; justify-self: center; width: calc(2 * var(--bar) + var(--gap)); outline: none; }
/* The baseline bar is solid and faded. Our bar has diagonal lines. */
.chart .bar { display: block; box-sizing: border-box; }
.chart .bar.base { background: var(--btn-border); }
.chart .bar.ours { border: 1.5px solid var(--fg); background: repeating-linear-gradient(135deg, var(--fg) 0 1.5px, transparent 1.5px 6px); }
.pair .bar { position: absolute; bottom: 0; width: var(--bar); transition: height 0.8s cubic-bezier(0.4, 0, 0.2, 1); }
.pair .base { left: 0; height: calc(var(--a) / var(--max) * 100%); }
.pair .ours { right: 0; height: calc(var(--b) / var(--max) * 100%); border-bottom: 0; }
/* The arrow: from above the baseline bar, across, and down to the top of our bar. */
.pair .drop {
  position: absolute;
  box-sizing: border-box;
  left: calc(var(--bar) / 2);
  right: calc(var(--bar) / 2 - 0.75px);
  bottom: calc(var(--b) / var(--max) * 100% + 4px);
  height: calc((var(--a) - var(--b)) / var(--max) * 100% + 2px);
  border: solid var(--fg);
  border-width: 1.5px 1.5px 0 0;
}
.pair .drop::after { /* arrow head */
  content: "";
  position: absolute;
  right: -4.25px;
  bottom: 1.5px;
  width: 7px;
  height: 7px;
  border: solid var(--fg);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
/* The decrease in percent, above the arrow. On hover and focus it gives way to the two values. */
.chart .pct { position: absolute; left: 50%; bottom: calc(var(--a) / var(--max) * 100% + 12px); transform: translateX(-50%); font-weight: 500; white-space: nowrap; }
.pct span + span,
.pair:hover .pct span:first-child,
.pair:focus .pct span:first-child { display: none; }
.pair:hover .pct span + span,
.pair:focus .pct span + span { display: inline; }
.pair .drop,
.chart .pct { transition: opacity 0.4s ease 0.8s; }
.chart.wait .pair .bar { height: 0; }
.chart.wait .drop,
.chart.wait .pct { opacity: 0; transition: none; }
.chart-names { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 8px 0 0 28px; font-size: 14px; line-height: 18px; text-align: center; }
/* For screen readers only. (Put a table inside a div with this class: a table ignores the 1 px width.) */
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* ---------- 04 Results: the gallery ----------
   A viewer for one result at a time: [input, with its control as inset] -> [output], the text
   prompt, Back / Next, and a strip of small pictures to pick a result. It uses .vslot and
   .input-pair of the cover, and .story-nav of the method figure. */
.gal-head,
.gal-row { display: grid; grid-template-columns: minmax(0, 1fr) 24px minmax(0, 1fr); gap: 8px 10px; align-items: center; margin: 0; }
.gal-head { margin-bottom: 8px; text-align: center; }
.gal-row .strip-arrow { justify-self: center; }
.gal-row .vslot video { background: var(--placeholder); }
.gal-row figcaption { grid-column: 1 / -1; min-height: 60px; font-size: 14px; line-height: 20px; color: var(--muted); } /* room for 3 lines: the strip below does not move */
.gallery .story-nav { justify-content: flex-end; margin-top: 4px; }
.gal-thumbs { display: grid; grid-template-columns: repeat(10, minmax(0, 1fr)); gap: 6px; margin-top: 16px; }
.gal-thumbs button { padding: 0; border: 0; background: var(--placeholder); cursor: pointer; opacity: 0.55; transition: opacity 0.2s ease; }
.gal-thumbs img { display: block; width: 100%; height: auto; }
.gal-thumbs button:hover,
.gal-thumbs button[aria-pressed="true"] { opacity: 1; }
.gal-thumbs button[aria-pressed="true"] { outline: 2px solid var(--fg); outline-offset: 2px; }

@media (max-width: 720px) {
  .header-inner { flex-direction: column; align-items: stretch; gap: 12px; }
  .cover-tiles .levels { flex: 1; } /* full width, the same as in the header */
  .levels button,
  .levels .agents { min-width: 0; }
}

/* ---------- Sections ---------- */
.section { padding-top: 80px; }
.section-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-bottom: 12px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--rule);
}
.section-head h2 {
  font: 400 14px/20px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}
.tag {
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
}
.section .wrap > * + *,
.section .wide { margin-top: 24px; }
.section .wrap + .wrap { margin-top: 24px; }

/* ---------- Footer ---------- */
footer.wrap { margin-top: 72px; } /* footer.wrap: .wrap alone would reset the margin to 0 */
.footer-inner {
  display: flex;
  justify-content: space-between;
  padding: 16px 0 48px;
  border-top: 1px solid var(--rule);
  font: 11px/16px var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
}
.footer-inner a { text-decoration: none; }
.footer-inner a:hover { color: var(--fg); }
.footer-links { display: flex; gap: 20px; }

/* ---------- Reading levels ---------- */
body[data-level="0"] .site-header,
body[data-level="0"] main,
body[data-level="0"] footer,
body[data-level="1"] [data-min="2"],
body:not([data-level="1"]) [data-only="1"] { display: none; }

@keyframes reveal {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}
.reveal { animation: reveal 0.45s ease both; }

/* ---------- Small screens ---------- */
@media (max-width: 560px) {
  .header-inner { padding-top: 14px; padding-bottom: 12px; }
  .levels button,
  .levels .agents { padding: 7px 6px 8px; white-space: nowrap; }
  .levels b { font-size: 11px; letter-spacing: 0.02em; }
  .levels small { letter-spacing: 0; }
  .tldr { padding-top: 32px; }
  .lead { margin-bottom: 28px; font-size: 20px; line-height: 28px; }
  /* TL;DR results: the input on top, the arrow, then the 2 x 2 outputs. */
  .results { grid-template: "in" "lin" "arrow" "out" "lout" / minmax(0, 1fr); }
  .results > .vslot { aspect-ratio: 832 / 480; }
  .results .strip-arrow { transform: rotate(90deg); }
  .cover h1 { font-size: 28px; }
  .cover-tiles { margin-top: 40px; }
  .reel { --gap: 4px; --crop: 46px; --edge: 36px; --mid: 26px; }
  .tie b { padding: 2px 6px; font-size: 10px; letter-spacing: 0.02em; }
  .imply b { padding: 0 6px; font-size: 10px; line-height: 14px; letter-spacing: 0.02em; }
  .ctx b { font-size: 9px; letter-spacing: 0.02em; }
  .headline { font-size: 20px; line-height: 27px; }
  .chart-panels { grid-template-columns: minmax(0, 1fr); }
  .gal-thumbs { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .gal-row figcaption { min-height: 100px; }
  .section { padding-top: 56px; }
}

/* 03 Method on a narrow screen: the scene scrolls sideways, so the text of the step cannot
   stay on it. The text goes below the scene; all texts share one grid cell. */
@media (max-width: 680px) {
  .story-notes { position: static; display: grid; margin-top: 12px; }
  .story-notes p { position: static; grid-area: 1 / 1; width: auto; font-size: 16px; line-height: 24px; }
}

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .tldr.play .lead > span,
  .tldr.play .results { animation: none; }
  .story svg *,
  .story-notes p,
  .story-fn,
  .pair .bar,
  .pair .drop,
  .chart .pct { transition: none; animation: none; }
}
