/* PlayTrain project page.
   Layout follows the Nerfies project page (nerfies.github.io): centered hero
   with the paper title, author line, and pill link buttons; a teaser figure with
   a subtitle caption under it; then centered section titles over a narrow
   justified column, with figures allowed to run wider. Written as plain CSS
   rather than pulling in Bulma, so the page has no dependency but the font. */

:root {
  color-scheme: light;   /* pin light: no dark repaint of form controls or scrollbars */
  --ink: #363636;
  --ink-strong: #1f1f1f;
  --muted: #6b6b6b;
  --line: #e4e4e4;
  --soft: #f7f7f7;
  --link: #3273dc;
  --button: #363636;
  --paper: #ffffff;
  /* The title and author face, as nerfies uses it. Google Fonts does serve
     Google Sans (fonts.gstatic.com/s/googlesans/v70) -- it just has to be
     REQUESTED. nerfies' own stylesheet declares the family but its font link is
     the legacy `css?family=Google+Sans` form; ours asks css2 for the four
     weights the page uses. Declaring the family without loading it is what made
     the header render in the body face. */
  --display: "Google Sans", "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --sans: "Noto Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.7 var(--sans);
}

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
b, strong { font-weight: 600; color: var(--ink-strong); }

.container {
  width: calc(100% - 3rem);
  max-width: 62rem;
  margin: 0 auto;
}
.container.is-narrow { max-width: 46rem; }

/* ---- section rhythm --------------------------------------------------------
   These were missing entirely, so every `<section class="section">` had zero
   padding and every `<h2 class="title">` fell back to the browser's default h2
   (1.5em, 0.83em margins). Values are the Bulma defaults the Nerfies page is
   built on, which is the spacing this layout was copied from:
     .section              padding: 3rem 1.5rem   (side gutter lives on .container here)
     .title.is-3           font-size: 2rem
     .title:not(:last-child)  margin-bottom: 1.5rem
     .content p:not(:last-child)  margin-bottom: 1em
   Body line-height stays at our 1.7 rather than Bulma's 1.5: this page is one
   narrow justified column and the looser leading is easier to read. */
.section { padding: 3rem 0; }
.section + .section { padding-top: 1rem; }

.title {
  /* nerfies uses Noto Sans (its body face) for section headings and reserves
     Google Sans for the paper title and author line. */
  text-align: center;          /* as the Nerfies template does */
  font-size: 2rem;
  line-height: 1.22;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--ink-strong);
  margin: 0 0 1.5rem;
}
.title:last-child { margin-bottom: 0; }

/* The markup already carries Bulma's text-alignment helpers (has-text-justified
   on the abstract), but the rules came from Bulma and were never brought over,
   so the justified column had been rendering ragged-right. */
.has-text-justified { text-align: justify; }
.has-text-centered { text-align: center; }

.content p { margin: 0 0 1em; }
.content p:last-child { margin-bottom: 0; }

/* Section lead-ins under a title. They sat tight against the following grid. */
.caption {
  /* Set as body prose, matching the Abstract: same 17px size, same ink, same
     1.7 leading and justified measure. These were 0.95rem in --muted grey,
     which read as small print under the heading rather than as text. */
  max-width: 46rem;
  margin: 0 auto 1.8rem;
  text-align: justify;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--ink);
}
/* Body text sits as a left-aligned block. Section titles stay centred. */
/* The header strip only needs a legend for the numbers, not an explanation. */
.caption.is-small { max-width: 40rem; margin-bottom: 0; font-size: 0.82rem;
  text-align: center; }

/* The author row is plain text in the body ink: no links, no link colour. Both
   addresses stay reachable from the footer's Contact line. */
.publication-authors { color: var(--ink); }

code {
  font: 0.85em/1.5 var(--mono);
  background: var(--soft);
  border-radius: 3px;
  padding: 0.1em 0.35em;
  color: var(--ink-strong);
}

pre {
  font: 0.78rem/1.7 var(--mono);
  background: var(--soft);
  border-radius: 5px;
  padding: 1.1rem 1.3rem;
  overflow-x: auto;
  margin: 0 0 1.2rem;
  color: var(--ink-strong);
}
pre code { background: none; padding: 0; font-size: 1em; }

/* ---- hero ---- */

.hero { padding: 3.5rem 0 1.5rem; text-align: center; }

.publication-title {
  font-family: var(--display);
  /* Bulma's .title + .title.is-1, the classes the reference h1 carries:
     3rem / 600 / 1.125, colour #363636, and no letter-spacing tweak. The
     tighter tracking and 700 weight were ours, and read heavier than nerfies. */
  font-size: 3rem;
  line-height: 1.125;
  font-weight: 600;
  color: var(--ink);
  margin: 0 auto 1.5rem;
  /* Our one deliberate departure: nerfies lets the title span the container,
     but its title is six words and ours is fourteen. */
  max-width: 46rem;
  word-break: break-word;
}

.publication-authors { font-family: var(--display);
  font-size: 1.25rem;
  margin-bottom: 0.3rem;
}
.publication-authors .author-block { display: inline-block; margin: 0 0.15rem; }
.publication-authors sup { font-size: 0.7em; }

.publication-affiliations {
  font-size: 1rem;
  color: var(--ink);
  margin-bottom: 1.6rem;
}
/* One run of text, as the reference has it, rather than four widely spaced
   blocks: the comma comes from CSS so the markup stays a plain list. */
.publication-affiliations span { display: inline-block; margin: 0 0.12rem; }
.publication-affiliations span:not(:last-child)::after { content: ","; }
.publication-affiliations sup { font-size: 0.75em; }

.publication-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
}
.link-button {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  background: var(--button);
  color: var(--paper);
  font-size: 0.92rem;
  font-weight: 500;
  padding: 0.45rem 1.3rem;
  border-radius: 999px;
}
.link-button:hover { text-decoration: none; opacity: 0.82; }
/* Pill glyphs: inline SVG rather than an icon font, so the page keeps its single
   external dependency. They inherit the pill's colour and track its font-size. */
.pill-icon {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
}
/* academicons draws the arXiv chi small inside a 448x512 box, so at a shared 1em
   it reads lighter than the octicon and the Font Awesome glyph beside it.
   Scaled up for optical balance, not because the box is a different size. */
.pill-icon.is-arxiv { width: 1.2em; height: 1.2em; }

/* Two repo links as ONE pill. The halves are still separate anchors -- a nested
   <a> is invalid and a JS-driven dropdown would be worse -- so the grouping is
   done with border radii: round on the outer edges, square where they meet,
   with a hairline between. Hover lights only the half under the cursor. */
.link-group { display: inline-flex; }
.link-button.is-grouped { border-radius: 0; }
.link-group > .is-grouped:first-child {
  border-radius: 999px 0 0 999px;
  padding-right: 0.9rem;
}
.link-group > .is-grouped:last-child {
  border-radius: 0 999px 999px 0;
  padding-left: 0.9rem;
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.28);
}
/* Placeholder pill: reads as present but not yet available. Not an <a>, so
   there is nothing to click and nothing to 404. */
.link-button.is-soon { opacity: 0.4; cursor: default; }
.link-button.is-soon:hover { opacity: 0.4; }

/* Lead line above the Abstract heading, set as one bordered box. Centring it
   left four ragged lines with a short last one, which read as four separate
   thoughts; a box with flush-left text reads as a single statement. */
.tldr {
  margin: 0 0 2.2rem;
  padding: 1.1rem 1.3rem;
  /* Tinted in the page's own link blue rather than a new hue, so the box picks
     up a colour already on the page. Text stays body ink: blue-on-blue at this
     size would drop the contrast well below AA. */
  background: #eef4fd;
  border: 1px solid #c7dbf7;
  border-radius: 6px;
  font-size: 1.02rem;
  line-height: 1.6;
  text-align: left;
  /* Keeps "node." from sitting alone on a fourth line. */
  text-wrap: pretty;
  color: var(--ink);
}
.tldr b { font-weight: 600; color: var(--link); }

/* ---- teaser ---- */

.teaser { padding: 2rem 0 1rem; }
.teaser img {
  display: block;
  width: 100%;
  max-width: 50rem;
  height: auto;
  margin: 0 auto;
  background: #fff;
  border-radius: 4px;
}
.teaser .subtitle {
  max-width: 44rem;
  margin: 1.4rem auto 0;
  font-size: 0.98rem;
  font-weight: 400;
  line-height: 1.6;
  color: var(--muted);
  text-align: center;
}

/* Header strip: a row of captioned clips under the title, the way the Nerfies
   teaser presents its four panels. Four across on a desktop, two on a phone. */
.teaser-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.7rem 0.7rem;
  max-width: 48rem;
  margin: 0 auto;
}
.teaser-strip figure { margin: 0; max-width: none; }
.teaser-strip img, .teaser-strip video {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0;
  background: #000;
  image-rendering: pixelated;
}
.teaser-strip figcaption {
  margin-top: 0.35rem;
  margin-bottom: 0.35rem;
  font-size: 0.85rem;
  text-align: center;
  color: var(--ink);
}
@media (max-width: 46rem) { .teaser-strip { grid-template-columns: repeat(2, 1fr); } }

/* ---- clip cards: human / IMPALA / PPO side by side ---- */

.pair-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.6rem 2rem;
  max-width: 52rem;
  margin: 1.6rem auto 0;
}
/* The full catalog runs three cards across; the comparison keeps two. */
.pair-grid.is-wide { grid-template-columns: repeat(3, 1fr); max-width: 58rem; gap: 1.4rem; }

.pair h3 {
  margin: 0 0 0.5rem;
  font-size: 0.9rem;
  font-weight: 600;
  font-family: var(--mono);
  color: var(--ink-strong);
  overflow-wrap: anywhere;
}
.pair-clips { display: flex; gap: 0.45rem; }
.pair-clips figure { margin: 0; max-width: none; flex: 1 1 0; min-width: 0;
  position: relative; }


.pair-clips img, .pair-clips video, .pair-clips canvas {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: #000;
  image-rendering: pixelated;
}
.pair-clips figcaption {
  margin-top: 0.3rem;
  font: 0.68rem/1.35 var(--mono);
  text-align: center;
  color: var(--muted);
}
.pair-clips figcaption small { display: block; font-size: 0.95em; color: var(--ink); }

/* The page's only meaningful colour: who is playing, and which policy won.
   The winner is marked by weight and a filled rule, not by hue alone. */
.pair-clips .is-human img, .pair-clips .is-human video, .pair-clips .is-human canvas { border-color: #9a9a9a; }
.pair-clips .is-agent img, .pair-clips .is-agent video { border-color: #c4d4ef; }
.pair-clips .is-best img, .pair-clips .is-best video { border-color: var(--link); border-width: 2px; }
.pair-clips .is-best figcaption { color: var(--link); font-weight: 600; }
.pair-clips .is-best figcaption small { color: var(--link); }

/* The header set runs four cards across, two rows of four. That needs more width
   than the 62rem container allows -- inside it a card would be ~195px and each of
   its two clips ~95px -- so the teaser section alone breaks out to 80rem. Clips
   land at ~148px and the whole eight-game strip is 740px, which fits one 14"
   screen instead of the ~2048px the two-across version took. */
.teaser .container { max-width: 80rem; }
.pair-grid.is-teaser { grid-template-columns: repeat(4, 1fr); max-width: 80rem; gap: 1.8rem 1.5rem; }
.teaser .pair h3 { font-size: 0.8rem; }
.teaser .pair-clips { gap: 0.35rem; }
.teaser .pair-clips figcaption { font-size: 0.62rem; }

/* Below the breakout width, stop forcing 80rem; below the container width, fall
   back to two across rather than squeezing four. */
@media (max-width: 84rem) { .pair-grid.is-teaser { max-width: 100%; } }
@media (max-width: 62rem) {
  .pair-grid.is-wide { grid-template-columns: repeat(2, 1fr); }
  .pair-grid.is-teaser { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 46rem) {
  .pair-grid, .pair-grid.is-wide { grid-template-columns: 1fr; }
}

/* ---- the in-page player ---- */

.player {
  max-width: 44rem;
  margin: 0 auto;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: var(--soft);
  padding: 0.9rem 1rem 1rem;
}

.player-bar {
  display: flex;
  align-items: baseline;
  gap: 0.8rem;
  font: 0.78rem var(--mono);
  color: var(--muted);
  margin-bottom: 0.6rem;
}
.player-name { color: var(--ink-strong); font-weight: 500; }
.player-hud { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player-bar button {
  font: 0.72rem var(--mono);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.22rem 0.7rem;
  cursor: pointer;
}
.player-bar button:hover { border-color: var(--muted); }

.player-stage { display: flex; justify-content: center; }

/* One canvas, centred. The games are square, so cap it or the picker below
   drops off the fold on a laptop. */
.player-pane { position: relative; width: 100%; max-width: 28rem; }
.player-pane canvas {
  display: block;
  /* fitCanvas() in player.mjs sets an explicit px width (a whole multiple of
     the render size), which is narrower than the 28rem pane, so the canvas
     needs centring in its own right -- the flex centring on .player-stage
     only centres the pane. */
  margin-inline: auto;
  width: 100%;
  height: auto;
  background: #000;
  border-radius: 3px;
  image-rendering: pixelated;
}
.player-pane.live canvas { outline: 2px solid var(--link); outline-offset: -2px; }

.player-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  background: rgba(10, 10, 10, 0.55);
  border-radius: 3px;
  cursor: pointer;
  color: #fff;
  text-align: center;
}
.player-overlay[hidden] { display: none; }
.player-play { font-size: 1.05rem; font-weight: 600; }
.player-keys { font: 0.72rem var(--mono); color: #d2d2d2; }

/* ---- touch devices: the games need a keyboard -----------------------------
   Every bundled game reads arrow keys, WASD and space and nothing else, so on a
   phone or tablet there is no way to play one. Without this, a visitor got a
   "Click to play" overlay that dismissed itself and left a frozen canvas, which
   reads as a broken page rather than an unsupported one.

   Keyed on INPUT CAPABILITY, not viewport width: a narrow desktop window has a
   keyboard and should keep the player, while a tablet in landscape is wider
   than any width breakpoint would catch and still has none. */
.player-touch { display: none; }
.player-controls-touch { display: none; }
.clips-mobile-note { display: none; }
@media (hover: none) and (pointer: coarse) {
  .clips-mobile-note { display: block; margin: 0 auto 0.9rem; }
  .player-overlay { cursor: default; gap: 0.5rem; }
  .player-play, .player-keys { display: none; }
  .player-touch {
    display: block;
    max-width: 20rem;
    padding: 0 1rem;
    font-size: 0.88rem;
    line-height: 1.5;
    font-weight: 500;
  }
  .player-controls-keys { display: none; }
  .player-controls-touch { display: inline; }
}

/* Always-visible control hint. The overlay hint vanishes on click-to-play, so
   this is the only thing that says which keys work while you are playing. */
.player-controls {
  margin: 0.6rem 0 0;
  font: 0.75rem/1 var(--mono);
  color: var(--muted);
  text-align: center;
}
.game-library { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: 0.9rem; }

/* Grouped picker: a small label over each row so the catalog reads as four
   kinds of game rather than one long undifferentiated strip. */
.chip-group + .chip-group { margin-top: 0.7rem; }
.chip-group-title {
  display: block;
  font: 600 0.68rem/1 var(--mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.35rem;
  text-align: center;
}
.chip-group[hidden] { display: none; }
.game-filter { display: block; text-align: center; }
.game-filter input {
  font: 0.78rem var(--sans);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.3rem 0.8rem;
  width: 11rem;
  text-align: center;
}
.game-filter input:focus { outline: none; border-color: var(--link); }

/* Names, not buttons: 33 bordered chips read as a wall, a centred index reads
   as a catalogue. The current game is the only one that carries weight. */
/* The game list reads as a block of names, not centred rows. */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 0.15rem 0.9rem;
}
.player-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.15rem 0.9rem;
  margin-top: 0.8rem;
}
.chip {
  font: 0.76rem/1.7 var(--mono);
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  cursor: pointer;
}
.chip[hidden] { display: none; }
.chip:hover { color: var(--link); }
.chip.on {
  color: var(--ink-strong);
  font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--link);
}

.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;
}

/* ---- footer ---- */

.footer {
  margin-top: 2rem;
  padding: 2.4rem 0 3rem;
  border-top: 1px solid var(--line);
  text-align: center;
}
.footer p {
  font-size: 0.85rem;
  line-height: 1.65;
  color: var(--muted);
  margin: 0 0 0.6rem;
}

@media (max-width: 46rem) {
  body { font-size: 16px; }
  .publication-title { font-size: 1.9rem; }
  .hero { padding-top: 2.2rem; }
  .player-stage { flex-direction: column; }
  .game-library-head { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
  .content.has-text-justified p, figcaption { text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* ---- learning curves ---- */
/* Two series, so the legend is always present; the panels also carry an end dot
   in the series colour, and the table below repeats the numbers. Identity is
   never carried by colour alone. */

.curve-legend {
  display: flex;
  justify-content: center;
  gap: 1.2rem;
  margin: 0 0 1rem;
  font: 0.8rem var(--mono);
  color: var(--ink);
}
.curve-key { display: inline-flex; align-items: center; gap: 0.4rem; }
.curve-key i { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }

/* The curve now sits inside its game's card, under the two clips. */
/* The curve sits in a <figure>, which carries the UA default 40px side margins --
   so the panel rendered 80px narrower than the two clips above it (222px in a
   302px card). Reset it so a game's curve spans exactly its own pair of clips. */
.pair .curve-cell { margin: 0; max-width: none; }
.pair .curve { margin-top: 0.5rem; }
.curve-legend { margin-top: 1rem; }
.curve { display: block; width: 100%; height: auto; overflow: visible; }
.curve-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
/* Per-seed spread for a trainer: its own colour, faint, behind its mean line. */
.curve-band { stroke: none; opacity: 0.16; }
/* Human reference. Grey and dotted rather than a third hue: the two series
   colours are a validated pair and a third would erode the CVD separation.
   Dotted + a band also reads as "reference level", not "another run". */
.curve-human { stroke: #5f5f5f; stroke-width: 1.4; stroke-dasharray: 4 3; fill: none; }
.curve-human-band { fill: #5f5f5f; opacity: 0.10; }
.curve-key i.is-human { background: none; height: 0; border-top: 2px dotted #5f5f5f; }
.curve-key i.is-band { background: #9aa7b4; height: 8px; border-radius: 2px; opacity: 0.5; }
.curve-end { stroke: var(--paper); stroke-width: 2; }        /* surface ring */
.curve-grid { stroke: var(--line); stroke-width: 1; }
.curve-axis { stroke: #c9c9c9; stroke-width: 1; }
.curve-tick { font: 9px var(--mono); fill: var(--muted); }
.curve-tick-end { text-anchor: end; }
.curve-tick-y { text-anchor: end; }

.curve-table { margin: 1.6rem auto 0; max-width: 44rem; font-size: 0.82rem; }
.curve-table summary { cursor: pointer; color: var(--muted); }
.curve-table table { margin-top: 0.7rem; font-size: 0.8rem; }
.curve-table td:first-child { font-family: var(--mono); }
.curve-table td:not(:first-child), .curve-table th:not(:first-child) { text-align: right; }

@media (max-width: 62rem) { .curve-grid-wrap { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 40rem) { .curve-grid-wrap { grid-template-columns: 1fr; } }

/* ---- catalog rail ---------------------------------------------------------
   34 games as a 3-across grid ran twelve rows deep and buried everything after
   it. One horizontal rail instead: native CSS scroll-snap, so touch swipe and
   trackpad drag work with no library, and the arrows just set scrollLeft.
   Cards keep the same .pair markup the header and comparison use. */
.rail-wrap { position: relative; margin-top: 1.6rem; }

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 19rem;
  gap: 1.6rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding: 0 1rem;
  padding: 0.25rem 0.25rem 1rem;
  /* a thin, quiet scrollbar: it is the affordance that says "this scrolls" */
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.rail::-webkit-scrollbar { height: 8px; }
.rail::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
.rail::-webkit-scrollbar-track { background: transparent; }
.rail:focus-visible { outline: 2px solid var(--link); outline-offset: 4px; }
.rail > .pair { scroll-snap-align: center; }

/* Arrows sit outside the cards on a wide screen and are hidden where a swipe is
   the obvious gesture. */
.rail-nav {
  position: absolute;
  top: 38%;
  z-index: 2;
  width: 3.6rem;
  height: 3.6rem;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink-strong);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
  /* Flex-centre the glyph rather than trusting line-height: the chevrons have
     asymmetric side bearings and no descender, so a line-height box left them
     sitting high and slightly left of the circle's centre. padding:0 keeps the
     button's own box from re-introducing an offset. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font: 1.9rem/1 var(--sans);
  cursor: pointer;
}
.rail-nav:hover { border-color: var(--ink); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18); }
/* Fully outside the rail so a card is never covered. The teaser/rail section
   breaks out to 80rem inside a wider viewport, leaving room in the margin. */
.rail-nav.is-prev { left: -4.6rem; }
.rail-nav.is-next { right: -4.6rem; }
.rail-nav[disabled] { opacity: 0.3; cursor: default; }
@media (max-width: 46rem) {
  .rail-nav { display: none; }
  .rail { grid-auto-columns: 15rem; }
}
