/* Pictura project page — styles.
   Loaded by index.html; the only inline CSS left there is none. The theme is set
   pre-paint by the small script in <head>, which stamps data-theme on <html>. */

/* ---------- self-hosted fonts ----------
   DM Sans and Source Serif 4 as variable woff2, served from assets/fonts/ instead of
   the Google Fonts CDN: no third-party request, and the page renders offline.
   unicode-range keeps the latin-ext files from being fetched unless a character
   in that range actually appears. */
@font-face {
  font-family: 'DM Sans';
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url(fonts/dmsans-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans';
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url(fonts/dmsans-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/dmsans-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/dmsans-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url(fonts/sourceserif-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: italic;
  font-weight: 400 600;
  font-display: swap;
  src: url(fonts/sourceserif-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/sourceserif-normal-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/sourceserif-normal-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --teal: #0C8375;
  --teal-deep: #0B5F56;
  --teal-fill: #D7F2ED;
  --indigo: #6C63D6;
  --indigo-deep: #423DA0;
  --link: #423DA0;
  --indigo-fill: #EAE9FC;
  --bg: #ffffff;
  --bg-alt: #f5f6f8;
  --card: #ffffff;
  --fg: #1c1e21;
  --fg-soft: #5a6069;
  --rule: #e3e6ea;
  --chip-ring: rgba(0,0,0,.45);
  --shadow: 0 1px 2px rgba(16,24,40,.05), 0 10px 26px rgba(16,24,40,.07);
  --shadow-lg: 0 2px 6px rgba(16,24,40,.06), 0 22px 48px rgba(16,24,40,.12);
  --glow-a: rgba(12,131,117,.16);
  --glow-b: rgba(108,99,214,.16);
  --max: 960px;
  /* radius scale: card surfaces, media inside them, small chips, swatches */
  --r-card: 16px;
  --r-sub: 10px;
  --r-chip: 6px;
  --r-swatch: 3px;
  --sans: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --serif: "Source Serif 4", Georgia, "Times New Roman", serif;
}
:root[data-theme="dark"] {
  --teal: #3FC7B4;
  --teal-deep: #8FE3D6;
  --teal-fill: #10302C;
  --indigo: #9A93F0;
  --indigo-deep: #C3BEFB;
  --link: #9A93F0;
  --indigo-fill: #1B1A33;
  --bg: #101317;
  --bg-alt: #171b21;
  --card: #1a1e25;
  --fg: #e8eaed;
  --fg-soft: #a2a9b4;
  --rule: #282d35;
  --chip-ring: rgba(255,255,255,.5);
  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 10px 26px rgba(0,0,0,.35);
  --shadow-lg: 0 2px 6px rgba(0,0,0,.45), 0 22px 48px rgba(0,0,0,.5);
  --glow-a: rgba(63,199,180,.13);
  --glow-b: rgba(154,147,240,.15);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; overflow-x: clip; }
body {
  margin: 0;
  overflow-x: clip;
  /* The hero glow rides on the page background instead of a 130vw pseudo-element:
     an element that wide widens the document, and iOS scrolls to it even with
     overflow-x hidden. The painting box is the same 130% x 620px, so the two
     radial stops keep the geometry they had. */
  background-color: var(--bg);
  background-image:
    radial-gradient(42% 60% at 38% 62%, var(--glow-a) 0%, transparent 68%),
    radial-gradient(44% 60% at 66% 52%, var(--glow-b) 0%, transparent 70%);
  background-repeat: no-repeat;
  background-size: 130% 620px;
  background-position: center -120px;
  color: var(--fg);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}
main { max-width: var(--max); margin: 0 auto; padding: 0 22px 40px; }
section { margin: 0 0 84px; }
/* Link color comes from a variable rather than a theme-scoped `a` rule: a rule like
   `:root[data-theme="dark"] a` outranks `.btn` and `.authors a`, which turned every
   pill button and author name indigo in dark mode. */
a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 2px; }
p { margin: 0 0 18px; max-width: 74ch; }

/* ---------- hero ---------- */
header {
  position: relative;
  text-align: center;
  padding: 76px 22px 10px;
  max-width: var(--max);
  margin: 0 auto;
}
h1.title {
  font-family: var(--serif);
  text-wrap: balance;
  font-size: clamp(2rem, 5.4vw, 3.15rem);
  font-weight: 600;
  line-height: 1.14;
  letter-spacing: -0.018em;
  margin: 0 0 14px;
  max-width: none;
}
h1.title .name {
  font-style: italic;
  background: linear-gradient(100deg, var(--teal) 8%, var(--indigo) 92%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.subtitle {
  color: var(--fg-soft);
  font-size: 1.08rem;
  margin: 0 auto 30px;
  max-width: 620px;
}
.authors {
  font-size: 1.04rem;
  margin: 0 auto 14px;
  max-width: 800px;
  line-height: 1.8;
}
.authors sup { color: var(--teal); font-weight: 600; font-size: .68em; }
/* Author homepages: quiet in the hero, only the rule under the name marks the link. */
.authors a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  transition: color .15s, border-color .15s;
}
.authors a:hover { color: var(--link); border-color: currentColor; }
/* keep each full name on one line; wrapping happens only between authors */
.authors .author { white-space: nowrap; }
.affil {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 0 auto 30px;
  max-width: 780px;
  list-style: none;
  padding: 0;
}
.affil li {
  font-size: 0.845rem;
  line-height: 1.5;
  color: var(--fg-soft);
  background: var(--bg-alt);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 4px 13px;
  white-space: nowrap;
}
/* A default <sup> grows the line box upwards, which pushes the label off-center in
   the chip. Zero line-height plus a relative offset lifts the mark without it. */
.affil sup {
  color: var(--teal);
  font-weight: 700;
  margin-right: 2px;
  font-size: 0.72em;
  line-height: 0;
  vertical-align: baseline;
  position: relative;
  top: -0.32em;
}
/* The whole chip is the hit target; it lifts and picks up the accent on hover. */
.affil li:has(a) { padding: 0; transition: border-color .15s, transform .15s; }
.affil li:has(a):hover { border-color: var(--indigo); transform: translateY(-1px); }
.affil a { display: block; padding: 4px 13px; color: inherit; text-decoration: none; }
.affil li:hover a { color: var(--fg); }

.links { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--card);
  color: var(--fg);
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 600;
  box-shadow: var(--shadow);
  transition: border-color .18s, transform .18s, box-shadow .18s;
}
.btn:hover { border-color: var(--indigo); transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.btn.primary {
  border-color: transparent;
  background: linear-gradient(100deg, var(--teal) 0%, var(--indigo) 100%);
  color: #fff;
}
.btn.primary:hover { border-color: transparent; }
/* Dark mode lightens the gradient, so the label flips to the page background color. */
:root[data-theme="dark"] .btn.primary { color: var(--bg); }
.btn[aria-disabled="true"] { opacity: .45; pointer-events: none; box-shadow: none; }
.btn svg { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.btn svg.solid { fill: currentColor; stroke: none; }
/* Each icon's artboard carries ~5 of its 24 units as empty margin, so with equal
   padding the ink sits right of center. Pull the leading icon back by that inset
   (the play triangle is inset further, hence the extra nudge). */
.btn > svg:first-of-type { margin-left: -3px; }
.btn > svg.solid:first-of-type { margin-left: -5px; }

/* Abstract disclosure: a pill in the button row that unfolds the card below it. */
button.btn { font-family: inherit; cursor: pointer; }
.abstract-btn svg.chev { width: 13px; height: 13px; margin-left: -1px; margin-right: -3px; transition: transform .3s ease; }
.abstract-btn[aria-expanded="true"] { border-color: var(--indigo); }
.abstract-btn[aria-expanded="true"] svg.chev { transform: rotate(180deg); }
.abstract-panel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  margin-top: 0;
  transition: grid-template-rows .35s ease, opacity .25s ease, margin-top .35s ease;
}
.abstract-panel > div { overflow: hidden; }
.abstract-panel.open { grid-template-rows: 1fr; opacity: 1; margin-top: 22px; }
.abstract-card {
  max-width: 800px;
  margin: 0 auto;
  padding: 22px 26px 4px;
  text-align: left;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  box-shadow: var(--shadow);
}
.abstract-card .eyebrow {
  margin: 0 0 10px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--teal-deep);
}
:root[data-theme="dark"] .abstract-card .eyebrow { color: var(--teal); }
.abstract-card p { font-size: 0.97rem; max-width: none; }

/* ---------- headings ---------- */
h2 {
  font-family: var(--serif);
  font-size: clamp(1.5rem, 3vw, 1.9rem);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.015em;
  margin: 0 0 22px;
}
h2::before {
  content: "";
  display: block;
  width: 46px;
  height: 3px;
  border-radius: 3px;
  margin-bottom: 16px;
  background: linear-gradient(90deg, var(--teal), var(--indigo));
}
h2.centered { text-align: center; }
h2.centered::before { margin-left: auto; margin-right: auto; }
h3.sub {
  font-family: var(--serif);
  font-size: 1.2rem;
  font-weight: 600;
  margin: 44px 0 10px;
}
.lede {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.24rem;
  line-height: 1.55;
  color: var(--fg);
  max-width: 62ch;
  margin: 0 0 26px;
}
/* Emphasis inside italic text flips upright, so the names still stand out. */
.lede em { color: var(--teal-deep); font-style: normal; }
:root[data-theme="dark"] .lede em { color: var(--teal); }

/* ---------- figures ---------- */
figure { margin: 0; }
figure img, figure video { width: 100%; height: auto; display: block; border-radius: var(--r-sub); }
figcaption {
  color: var(--fg-soft);
  font-size: 0.88rem;
  line-height: 1.6;
  margin: 14px auto 0;
  max-width: 68ch;
  text-align: center;
}
.teaser { margin: 26px auto 48px; max-width: 860px; }

/* Teaser cross-fade: Pictura's render <-> Alberti's perspective construction */
.xfade {
  position: relative;
  container-type: inline-size;
  /* The render is 768x432; like the paper's teaser it is cropped 24px top and bottom. */
  aspect-ratio: 2 / 1;
  border-radius: var(--r-card);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  /* The render covers the whole box, so this only ever shows at the rounded
     corners -- it matches the card so no bright sliver appears there. The
     contain-fitted Alberti plate letterboxes onto the render, not onto this. */
  background: var(--card);
}
.xfade img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  border-radius: inherit;
}
.xfade .render { object-fit: cover; }
.xfade .plate  { object-fit: contain; }
/* The Alberti plate fades in over the render, then back out, on a loop. It never
   reaches full opacity, so the render still reads through the construction. */
.xfade .plate {
  opacity: 0;
  animation: teaserPlate 11s ease-in-out infinite;
}
@keyframes teaserPlate {
  0%, 45%   { opacity: 0; }
  52%, 68%  { opacity: .55; }
  75%, 100% { opacity: 0; }
}

/* Callouts on the render, matching the annotated teaser of the paper.
   Positions are the same fractions of the cropped frame used in figs/teaser.tex;
   each arrow starts at its label's anchor point, so the opaque chip hides the tail. */
.annot { position: absolute; inset: 0; animation: teaserCycleOut 11s ease-in-out infinite; }
.annot svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.annot b {
  position: absolute;
  left: var(--x);
  top: var(--y);
  transform: translate(var(--tx, 0), var(--ty, 0));
  padding: .18em .5em;
  border-radius: .28em;
  background: var(--c);
  color: #fff;
  font-size: clamp(8px, 1.65cqw, 13.5px);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: .005em;
  white-space: nowrap;
}
@keyframes teaserCycleOut {
  0%, 45%   { opacity: 1; }
  52%, 68%  { opacity: 0; }
  75%, 100% { opacity: 1; }
}
.xfade .plate-label {
  position: absolute;
  left: 14px;
  bottom: 14px;
  padding: 4px 12px;
  border-radius: 999px;
  background: rgba(28, 24, 12, .72);
  color: #f3ecd6;
  font-family: var(--serif);
  font-size: 0.8rem;
  font-style: italic;
  opacity: 0;
  animation: teaserCycle 11s ease-in-out infinite;
  pointer-events: none;
}
@keyframes teaserCycle {
  0%, 45%   { opacity: 0; }   /* the render is shown on its own */
  52%, 68%  { opacity: 1; }   /* the Alberti plate is briefly overlaid */
  75%, 100% { opacity: 0; }   /* fade back to the render */
}
@media (prefers-reduced-motion: reduce) {
  /* No auto-cycling: keep the annotated render, reveal Alberti on hover or focus. */
  .xfade .plate, .xfade .plate-label, .annot { animation: none; transition: opacity .4s ease; }
  .xfade:hover .plate, .xfade:focus-within .plate { opacity: .55; }
  .xfade:hover .plate-label, .xfade:focus-within .plate-label { opacity: 1; }
  .xfade:hover .annot, .xfade:focus-within .annot { opacity: 0; }
}


/* Figure chips: one card per panel, its explanation set inside the card. --w is the
   panel's exact aspect ratio and the flex basis is 0, so each panel's width lands at
   w/sum(w) of the row -- which makes every panel in a row the same height. */
.figchips {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin: 0 0 10px;
}
.figchip {
  flex: var(--w, 1) 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 16px 18px 2px;
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  box-shadow: var(--shadow);
}
.figchip img {
  width: 100%; height: auto; display: block;
  border-radius: var(--r-sub);
}
/* A chip whose explanation is in the body text instead of inside the card. */
.figchip.bare { padding: 16px 18px; }
/* Every figure ships as a transparent-background PNG in two cuts, swapped by the
   theme script: the plots have their lightness flipped (hue and saturation kept,
   so each series holds its published color), and the pipeline diagram is compiled
   twice from its TikZ source with a dark palette -- its camera thumbnails are left
   exactly as rendered, since they are dark to begin with. */
.figchip img { background: transparent; }
.figchip figcaption {
  margin: 14px 0;
  max-width: none;
  text-align: left;
  font-size: 0.85rem;
  line-height: 1.55;
}
/* A fused chip holds several panels side by side, each tagged; the widths come from
   the panels' aspect ratios so they end up at comparable heights. */
.figchip.full { flex-basis: 100%; }
.figchip .panels { display: flex; flex-wrap: wrap; gap: 18px; }
.figchip .panel { flex: var(--w, 1) 1 0; min-width: 0; }
/* Rule between panels, drawn in the middle of the gap so it costs no layout width
   and the panels keep their exact proportional widths (and so, equal heights). */
.figchip .panel + .panel { position: relative; }
.figchip .panel + .panel::before {
  content: "";
  position: absolute;
  left: -9px; top: 0; bottom: 0;
  border-left: 1px solid var(--rule);
}
/* Below this width proportional columns get too narrow to read: stack instead. */
@media (max-width: 760px) {
  .figchips, .figchip .panels { display: block; }
  .figchips > .figchip + .figchip, .figchip .panel + .panel { margin-top: 20px; }
  /* Stacked, the rule between panels lies down. */
  .figchip .panel + .panel::before { display: none; }
  .figchip .panel + .panel { border-top: 1px solid var(--rule); padding-top: 20px; }
}
.figchip .tag {
  display: inline-block;
  margin: 0 0 8px 2px;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--bg-alt);
  border: 1px solid var(--rule);
  color: var(--fg-soft);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
}
/* The same tag, set inline in a caption, so the text points at the panels by their
   own labels instead of by position. */
.figchip figcaption .tag {
  margin: 0 3px 0 0;
  padding: 1px 8px;
  font-size: 0.66rem;
  vertical-align: 1px;
}
.figchip .tag.is-example {
  background: var(--indigo-fill);
  border-color: transparent;
  color: var(--indigo-deep);
}

/* The panel's result, as a chip riding on top of the chip: what the figure says
   is the headline, the caption below only describes what is plotted. */
.figchip .headline {
  position: relative;
  /* Pulled out to the card's edges: the negative margins cancel the card padding,
     so the band sits flush and picks up the card's own top corners. */
  margin: -16px -18px 16px;
  max-width: none;   /* the global p { max-width: 74ch } would stop the band short */
  padding: 13px 18px 13px 38px;
  border-radius: calc(var(--r-card) - 1px) calc(var(--r-card) - 1px) 0 0;
  background: linear-gradient(100deg, var(--teal-fill), var(--indigo-fill));
  color: var(--fg);
  font-size: 1.02rem;
  font-weight: 400;
  line-height: 1.5;
}
/* A small accent dot opens the block, echoing the gradient of the section rules. */
.figchip .headline::before {
  content: "";
  position: absolute;
  left: 18px;
  top: 1.5em;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--teal), var(--indigo));
}
/* The title is set in the display serif, the detail after it stays in the sans. */
.figchip .headline b {
  font-family: var(--serif);
  font-size: 1.06em;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--teal-deep);
}
.figchip .headline em { font-style: italic; }

/* ---------- full-bleed band ---------- */
.band {
  position: relative;
  width: 100vw;
  left: 50%;
  transform: translateX(-50%);
  padding: 68px 22px 72px;
  margin-bottom: 84px;
  background: var(--bg-alt);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.band-inner { max-width: var(--max); margin: 0 auto; }

/* ---------- stats ---------- */
/* The four tiles go 4-across, then straight to 2x2, then 1 per row. auto-fit would
   pass through a 3+1 step, which leaves the fourth tile orphaned on its own row. */
.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
}
@media (max-width: 880px) { .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 420px) { .stats { grid-template-columns: minmax(0, 1fr); } }
.stat {
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  background: var(--card);
  padding: 22px 16px;
  text-align: center;
  box-shadow: var(--shadow);
}
.stat .num {
  display: block;
  font-family: var(--serif);
  font-size: 1.95rem;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--indigo-deep);
}
.stat .lbl { display: block; font-size: 0.92rem; color: var(--fg); margin-top: 6px; }
.stat .sub { display: block; font-size: 0.79rem; color: var(--fg-soft); margin-top: 4px; }
.stat.wide {
  grid-column: 1 / -1;
  background: linear-gradient(135deg, var(--teal-fill), var(--indigo-fill));
  border-color: transparent;
}
.stat.wide .num { color: var(--teal-deep); }
.stat.wide .lbl { max-width: 62ch; margin-inline: auto; }

/* ---------- clips ---------- */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 22px;
  align-items: start;
}
.clip {
  border-radius: var(--r-card);
  overflow: hidden;
  background: var(--card);
  box-shadow: var(--shadow);
  transition: transform .2s, box-shadow .2s;
}
.clip:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
/* The media rounds its own top corners rather than being cut by the card's clip:
   a squircle clip and a rectangular child leave a dark crescent at each corner. */
.clip img, .clip video {
  width: 100%; height: auto; display: block;
  image-rendering: pixelated;
  border-radius: var(--r-card) var(--r-card) 0 0;
}
/* clickable clip -> HD demo. The badge marks the affordance; the whole frame is the hit target. */
.clip a.hd { position: relative; display: block; cursor: zoom-in; }
.clip a.hd .hd-badge {
  position: absolute; top: 8px; right: 8px;
  font-size: 0.66rem; font-weight: 700; letter-spacing: 0.04em;
  padding: 2px 6px; border-radius: var(--r-chip);
  background: rgba(0,0,0,0.55); color: #fff;
  opacity: 0.75; transition: opacity .2s; pointer-events: none;
}
.clip a.hd:hover .hd-badge { opacity: 1; }
.clip .cap {
  background: var(--card);
  color: var(--fg-soft);
  font-size: 0.83rem;
  padding: 10px 14px;
  border-top: 1px solid var(--rule);
}
.clip .cap b { color: var(--fg); }

/* HD lightbox: full-screen overlay playing the higher-res demo render. */
.lightbox {
  position: fixed; inset: 0; z-index: 100;
  display: none; align-items: center; justify-content: center;
  background: rgba(6,8,12,0.9); padding: 24px;
}
.lightbox.open { display: flex; }
.lightbox video {
  max-width: 96vw; max-height: 82vh; width: auto; height: auto;
  border-radius: var(--r-sub); box-shadow: var(--shadow-lg); background: #000;
}
.lightbox .lb-cap {
  position: absolute; left: 0; right: 0; bottom: 22px;
  text-align: center; color: rgba(255,255,255,0.82); font-size: 0.86rem;
}
.lightbox .lb-close {
  position: absolute; top: 16px; right: 20px;
  width: 40px; height: 40px; border: 0; border-radius: 50%;
  background: rgba(255,255,255,0.12); color: #fff; font-size: 1.4rem; line-height: 1;
  cursor: pointer;
}
.lightbox .lb-close:hover { background: rgba(255,255,255,0.22); }

.legend {
  display: flex; flex-wrap: wrap; gap: 8px 20px; justify-content: center;
  font-size: 0.85rem; color: var(--fg-soft);
  max-width: none;
  margin: 0 0 20px;
}
.legend span { display: inline-flex; align-items: center; gap: 7px; }
/* Boxes are drawn as faces, so their swatch is a square whose gradient spans the
   family's per-face hues; road markings are drawn as lines, so theirs is a stroke
   of the rasterizer's own width, on the black the renderer draws them over. */
.legend i {
  display: inline-block; flex: none;
  border: 1px solid var(--chip-ring);
}
.legend i.box { width: 12px; height: 12px; border-radius: var(--r-swatch); }
.legend i.ln {
  width: 22px; height: 13px; border-radius: var(--r-swatch);
  background-color: #000;
  background-image: linear-gradient(var(--c), var(--c));
  background-size: 100% var(--w);
  background-position: center;
  background-repeat: no-repeat;
}
/* Camera-rig key: each cell carries a top-down wedge of that camera's coverage,
   drawn from the rig's real geometry (0 and +/-55 deg, ~64 deg horizontal FOV). */
.rig-key {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  width: min(300px, 100%);
  margin: 0 auto 20px;
  font-size: 0.72rem;
  text-align: center;
  color: var(--fg-soft);
}
.rig-key span {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  border: 1px solid var(--rule);
  border-radius: var(--r-chip);
  padding: 7px 2px 6px;
  background: var(--bg-alt);
}
.rig-key svg { width: 34px; height: 34px; display: block; }
.rig-key .ring { fill: none; stroke: var(--rule); stroke-width: 1; }
.rig-key .fov { fill: var(--teal); fill-opacity: .3; stroke: var(--teal); stroke-width: 1; stroke-linejoin: round; }
.rig-key .ego { fill: var(--fg-soft); }
.rig-key .empty { border-color: transparent; background: none; }
.note {
  font-size: 0.87rem;
  color: var(--fg-soft);
  background: var(--bg-alt);
  border: 1px solid var(--rule);
  padding: 13px 17px;
  border-radius: var(--r-sub);
  margin-top: 26px;
  max-width: none;
}
.note code {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-chip);
  padding: 1px 5px;
  font-size: 0.9em;
}

/* ---------- citation ---------- */
.cite-wrap { position: relative; }
pre {
  background: var(--card);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  padding: 22px;
  overflow-x: auto;
  font-size: 0.82rem;
  line-height: 1.6;
  margin: 0;
  box-shadow: var(--shadow);
}
code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.copy {
  position: absolute; top: 12px; right: 12px;
  border: 1px solid var(--rule); border-radius: var(--r-chip);
  background: var(--bg-alt); color: var(--fg-soft);
  font: inherit; font-size: 0.76rem; font-weight: 600; padding: 5px 12px; cursor: pointer;
  transition: color .15s, border-color .15s;
}
.copy:hover { color: var(--fg); border-color: var(--indigo); }
.namesake {
  font-family: var(--serif);
  font-size: 1.0rem;
  color: var(--fg-soft);
  max-width: 62ch;
  margin: 0 auto 14px;
}

footer {
  border-top: 1px solid var(--rule);
  color: var(--fg-soft);
  font-size: 0.88rem;
  text-align: center;
  padding: 30px 22px 46px;
  max-width: var(--max);
  margin: 0 auto;
}
footer a { font-weight: 600; }

.theme-toggle {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 50;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--card);
  color: var(--fg-soft);
  cursor: pointer;
  box-shadow: var(--shadow);
  transition: color .15s, border-color .15s, transform .15s;
}
.theme-toggle:hover { color: var(--fg); border-color: var(--indigo); transform: translateY(-1px); }
.theme-toggle:focus-visible { outline: 2px solid var(--indigo); outline-offset: 2px; }
.theme-toggle svg {
  display: none;
  width: 18px; height: 18px;
  fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.theme-toggle[data-mode="light"] .i-light,
.theme-toggle[data-mode="auto"]  .i-auto,
.theme-toggle[data-mode="dark"]  .i-dark { display: block; }

@media (max-width: 600px) {
  body { font-size: 16.5px; }
  /* The title's hard break after "Self-Play" fits down to ~560px; below that it
     would strand a word or two on the first line, so the title wraps on its own. */
  h1.title br { display: none; }
  header { padding-top: 56px; }
  section { margin-bottom: 64px; }
  .affil li { white-space: normal; }
}

/* ---------- squircle corners ----------
   Superellipse corners wherever the browser supports `corner-shape` (Chrome 139+);
   everywhere else these elements keep the plain rounded rectangles above, so the
   page degrades to exactly what it looked like before. Radii are nudged up a
   little here: a squircle of a given radius reads tighter than an arc of the same
   radius. Genuinely circular things -- the pill buttons, the affiliation chips,
   the theme toggle, the lightbox close -- keep their true round shape. */
@supports (corner-shape: squircle) {
  :where(
    .xfade,
    figure img, figure video,
    .figchip, .figchip img, .figchip .headline,
    .clip, .clip a.hd, .clip .hd-badge, .clip img, .clip video,
    .xfade img,
    .stat, .abstract-card, .note, pre, .copy,
    .rig-key span, .legend i, .annot b,
    .lightbox video
  ) {
    corner-shape: squircle;
  }
  :root { --r-card: 20px; --r-sub: 13px; --r-chip: 8px; --r-swatch: 4px; }
}
