/* THE CHRONICLE.
   The same cabinet, printed. Palette, font and CRT come from the game's own
   styles/tokens.css and styles/crt.css, which are loaded ahead of this file —
   so when somebody changes the spectrum, the book changes with it. All this
   file does is teach that language to a page you scroll instead of play.

   Generated by tools/chronicle.sh. Editing it by hand lasts until the next
   commit. */

/* tokens.css locks the viewport for a canvas; a book has to scroll. */
html, body { height: auto; overflow: visible; }
body {
  user-select: text;
  -webkit-user-select: text;
  padding: 0 1.25rem 6rem;
}
main {
  position: relative;
  z-index: 1;
  max-width: 44rem;
  margin: 0 auto;
  padding-top: clamp(3rem, 12vh, 7rem);
}

/* The title, borrowed straight off the splash screen. */
h1 {
  font-size: clamp(1.9rem, 7.5vw, 3.6rem);
  font-weight: 600;
  letter-spacing: 0.4em;
  margin-right: -0.4em;
  text-wrap: balance;
  background: linear-gradient(100deg,
    #ff3ec8, #ffb020, #b6ff3d, #21f3ff, #a04bff, #ff3ec8);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 16px rgba(255, 62, 200, 0.5));
  animation: slide 6s linear infinite;
}
@keyframes slide {
  from { background-position: 0% 50%; }
  to { background-position: 300% 50%; }
}

.sub {
  color: var(--dim);
  font-size: 0.8rem;
  line-height: 1.9;
  letter-spacing: 0.08em;
  margin-top: 1.1rem;
  max-width: 34rem;
}
/* The two ways in. A row on anything wide enough to hold both, and two rows
   the moment it is not - each of them one unbreakable phrase, so a narrow
   screen never gets THE LATEST on one line and CHAPTER on the next. */
.prompt {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem 2rem;
  margin: 1.6rem 0 3.2rem;
  font-size: 0.8rem;
  letter-spacing: 0.3em;
}
.prompt a { white-space: nowrap; }
/* The splash screen blinks this; a link you are meant to click does not. */
.prompt a {
  color: var(--lime);
  text-decoration: none;
  text-shadow: 0 0 12px currentColor;
}
.prompt a:hover { text-shadow: 0 0 20px currentColor; }
.heading {
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.34em;
  color: var(--magenta);
  text-shadow: 0 0 10px currentColor;
  margin: 3.4rem 0 1.4rem;
}
/* The one paragraph that says what a book is. It is under the heading rather
   than in it because the heading is a marquee and this is an explanation, and
   a reader only needs it once. */
.shelfnote {
  max-width: 62ch;
  margin: -0.7rem 0 0;
  font-size: 0.68rem;
  line-height: 1.7;
  color: var(--dim);
  text-wrap: pretty;
}

/* An instrument panel, same brackets as the field guide on the splash. */
.panel {
  position: relative;
  padding: 1.1rem 1.3rem 1.2rem;
  background: rgba(12, 5, 24, 0.66);
  border: 1px solid rgba(33, 243, 255, 0.35);
  box-shadow: 0 0 26px rgba(160, 75, 255, 0.28),
              inset 0 0 34px rgba(33, 243, 255, 0.06);
}
.panel::before,
.panel::after {
  content: "";
  position: absolute;
  width: 13px;
  height: 13px;
  border: 1px solid var(--magenta);
}
.panel::before { top: -1px; left: -1px; border-right: none; border-bottom: none; }
.panel::after { bottom: -1px; right: -1px; border-left: none; border-top: none; }
.panel h2 {
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.34em;
  color: var(--magenta);
  text-shadow: 0 0 10px currentColor;
  margin-bottom: 0.85rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px dashed rgba(160, 75, 255, 0.35);
}

.roster { animation: cycle 9s linear infinite; }
.roster table { width: 100%; border-collapse: collapse; font-size: 0.72rem; }
.roster th {
  text-align: left;
  font-weight: 400;
  letter-spacing: 0.18em;
  color: var(--dim);
  padding: 0.3rem 0.4rem;
}
.roster td {
  padding: 0.42rem 0.4rem;
  letter-spacing: 0.06em;
  border-top: 1px solid rgba(160, 75, 255, 0.18);
  font-variant-numeric: tabular-nums;
}
.roster td:first-child { color: var(--ink); }
.roster .pilot { display: flex; align-items: center; gap: 0.5rem; }
.roster td:nth-child(2) { color: var(--amber); }
.roster a { color: var(--cyan); text-decoration: none; }

/* The contents. Read the taglines straight down and you have the whole story
   of the cabinet; click one and you get the version it belongs to, whole. */
/* the row gap does the spacing now, and a margin as well as a gap puts the
   second one half an inch in from the first the moment they stack */
.prompt .read { color: var(--amber); }
.contents { list-style: none; }
.contents li { border-top: 1px solid rgba(160, 75, 255, 0.16); }
/* The number, the sentence, and the marks hard against the right edge — so the
   marks read down the page as their own column rather than trailing whatever
   length the sentence happened to be. */
.cv a {
  display: grid;
  grid-template-columns: 3.4rem minmax(0, 1fr) auto;
  gap: 0.9rem;
  align-items: baseline;
  padding: 0.62rem 0.3rem;
  text-decoration: none;
}
.cv .marks { justify-content: flex-end; }
.cv a:hover { background: rgba(160, 75, 255, 0.13); }
.cv b { font-size: 0.7rem; font-weight: 500; letter-spacing: 0.16em; color: var(--cyan); }
/* The tagline, and only the tagline. It used to be every span in the row,
   which was true right up until the marks arrived wearing spans of their own
   and came out amber and three sizes too big. */
.cv a > span:not(.marks) {
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--amber);
  text-wrap: pretty;
}
.cv a:hover > span:not(.marks) { text-shadow: 0 0 12px rgba(255, 176, 32, 0.5); }
/* The pilots' faces. One each, painted once by tools/chronicle-art.sh, and the
   same one everywhere their name is written — which is the reason it is worth
   painting at all. Round, because a portrait among all these straight lines
   should not read as one more panel, and because the helmet fills the frame
   and crops well. A pilot with no face costs nothing: the name simply sits
   where it always did. */
.face {
  flex: none;
  width: 3.1rem;
  height: 3.1rem;
  border-radius: 50%;
  object-fit: cover;
  background: #05010c;
  border: 1px solid rgba(160, 75, 255, 0.55);
  box-shadow: 0 0 10px rgba(160, 75, 255, 0.4);
}
/* A mention, in the margin of whichever version was on the cabinet while it
   happened. Two columns, the same shape a chapter row uses one line up: the
   sentence, and the marks hard against the right in a column of their own. The
   marks used to be floated instead, which reads correctly right up until the
   sentence is narrower than the float - and then the icons are standing in the
   middle of the words. A phone is always that narrow. */
.ci {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 0.2rem 0.7rem;
  padding: 0.5rem 0.3rem 0.5rem 4.3rem;
  font-size: 0.66rem;
  line-height: 1.6;
  color: var(--dim);
}
.ci b { color: var(--violet); font-weight: 500; }
.ci .what { grid-column: 1; }
/* Quieter than the deed above it and in the same proportion the chapter uses,
   because it is here to tell two interludes apart rather than to be read. */
.ci .said {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  color: var(--dim);
  opacity: 0.8;
}

/* --- the marks -------------------------------------------------------------
   Which parts of the cabinet a commit moved, drawn rather than listed. There
   are nine of them and most chapters wear two, so the row is short by
   arithmetic rather than by being cut off somewhere.

   Every mark carries its own words in the markup, in both forms. The tiny one
   hides them from the eye and not from a screen reader, because a row of
   little pictures with nothing behind them is a row of nothing at all to
   anybody who cannot see little pictures.

   The colours are ranked rather than assigned. This book is read for two
   questions before any other — did somebody change the game, and did somebody
   change the game behind it — so those two take the two loudest things in the
   spectrum and everything else takes what is left. The rules and the notes are
   dim on purpose, and for the same reason the drawn field will not light a
   rock that is not the game: real work, but not the thing you came to read
   about. */
.marks { display: flex; flex-wrap: wrap; align-items: center; gap: 0.34rem; }
.mk {
  display: inline-flex;
  align-items: center;
  gap: 0.36rem;
  padding: 0.16rem 0.5rem 0.16rem 0.4rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  background: rgba(5, 1, 12, 0.55);
  color: var(--dim);
  font-size: 0.52rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
}
.mk .ic { flex: none; width: 0.84rem; height: 0.84rem; }
.mk em { font-style: normal; }
/* the two loudest, and they are loud on purpose */
.mk-game   { color: var(--magenta); }
.mk-meta   { color: var(--lime); }
.mk-ui     { color: var(--cyan); }
.mk-music  { color: var(--amber); }
.mk-hands  { color: var(--violet); }
.mk-engine { color: var(--mint); }
.mk-book   { color: var(--ink); }
/* the rules and the notes are real work and not the game, and the field draws
   that distinction in exactly this colour */
.mk-rules, .mk-notes { color: var(--dim); }

/* On the splash, where the badges are met for the first time and are the only
   place in the book that says what they mean. */
.marks.big { margin-top: 1rem; gap: 0.45rem; }
.marks.big .mk {
  padding: 0.26rem 0.68rem 0.26rem 0.55rem;
  font-size: 0.58rem;
  box-shadow: 0 0 14px rgba(5, 1, 12, 0.8), inset 0 0 12px rgba(255, 255, 255, 0.04);
  text-shadow: 0 0 10px currentColor;
}
.marks.big .mk .ic { width: 0.95rem; height: 0.95rem; }

/* In a list, where a reader is scanning down a column and the words would be
   a second column arguing with the first. */
.marks.tiny { gap: 0.22rem; }
.marks.tiny .mk { padding: 0.2rem; border-radius: 50%; }
.marks.tiny em {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* An interlude keeps to the same right-hand column the chapters use. Half a
   column aligned and half of it trailing whatever length the sentence was
   reads as a mistake rather than as two kinds of entry. Placed rather than
   floated, for the reason written where .ci is defined. */
.ci .marks { grid-column: 2; grid-row: 1; justify-content: flex-end; }
.ci .said { grid-column: 1 / -1; }

/* --- the shelf -------------------------------------------------------------
   A book is a run of chapters one pilot flew with nobody else landing in
   between, and the shelf is every book there has been, newest on top. The one
   on the cabinet is open, because that is the one somebody arriving came for;
   everything under it is a spine you choose to pull.

   <details> does the whole of the opening and closing, so the shelf works on a
   page with the script switched off — which is the same promise every other
   moving part of this book makes. */
.shelf { display: grid; gap: 0.7rem; margin-top: 0.9rem; }
.book {
  border: 1px solid rgba(160, 75, 255, 0.32);
  background: linear-gradient(to right, rgba(160, 75, 255, 0.09), rgba(160, 75, 255, 0.02));
}
.book[open] { border-color: rgba(160, 75, 255, 0.6); }
.book > summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.55rem 0.95rem;
  padding: 0.7rem 0.9rem;
  cursor: pointer;
  list-style: none;
}
.book > summary::-webkit-details-marker { display: none; }
.book > summary::before {
  content: "\25b6";
  flex: none;
  font-size: 0.6rem;
  color: var(--violet);
  transition: transform 0.18s;
}
.book[open] > summary::before { transform: rotate(90deg); }
.book > summary:hover { background: rgba(160, 75, 255, 0.13); }
.bk { display: flex; align-items: baseline; gap: 0.45rem; }
.bk b {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--violet);
  text-shadow: 0 0 16px currentColor;
}
.bk i {
  font-style: normal;
  font-size: 0.46rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--dim);
}
.by {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-right: auto;
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  color: var(--ink);
}
.by .face { width: 2.1rem; height: 2.1rem; }
.bspan {
  padding: 0.12rem 0.46rem;
  border: 1px solid rgba(33, 243, 255, 0.45);
  font-size: 0.56rem;
  letter-spacing: 0.18em;
  color: var(--cyan);
  font-variant-numeric: tabular-nums;
}
.bn {
  font-size: 0.56rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--dim);
}
/* What the book was, in a paragraph — the arithmetic of the whole run, which
   is the one thing no single chapter inside it can say. */
.gist {
  max-width: 62ch;
  margin: 0 0.9rem 0.4rem;
  padding-left: 0.85rem;
  border-left: 1px solid rgba(255, 176, 32, 0.4);
  font-size: 0.66rem;
  line-height: 1.7;
  color: var(--dim);
  text-wrap: pretty;
}
.book .contents { padding: 0 0.9rem 0.5rem; }

/* --- one version, one comic page ------------------------------------------
   A chapter used to be one screenful read in a single look, and it fought the
   prose for room the whole time. It is a page of cels now: a splash for the
   tagline, the narration beside the numbers, the drawn commit under both, and
   a cel apiece for anything the book had to write down. No block of prose in
   here is wider than 600px, which is the whole reason there are several of
   them — a chapter is a handful of short things rather than one wide one.

   Everything sits on the same twelve columns, so the cels line up without
   anybody having to place them, and a cel with no neighbour this week takes
   the width its neighbour is not using. */
/* The dock is fixed to the foot of the window, so the page ends above it
   rather than under it: the last cel of a chapter is as readable as the
   first. */
body.page { padding: 0 0 clamp(6.5rem, 4rem + 6vh, 8.5rem); }
html { scroll-padding-bottom: 6rem; }
.page main {
  max-width: 82rem;
  margin: 0 auto;
  padding: clamp(0.8rem, 2.4vh, 1.6rem) clamp(0.7rem, 2.4vw, 2rem) 0;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(0.6rem, 1.1vw, 1rem);
  align-content: start;
}

/* A cel: hard border, black ink dropped behind it, a corner of the spectrum
   leaking in at the top. It arrives as you scroll onto it — the script adds
   .in, and adds it to everything at once for anybody who asked for less
   motion, so nothing is ever left hidden behind an animation. */
.cel {
  position: relative;
  grid-column: span 12;
  padding: 1.35rem 1.2rem 1.1rem;
  background: linear-gradient(168deg, rgba(160, 75, 255, 0.1), rgba(7, 3, 15, 0.88) 46%);
  border: 2px solid rgba(160, 75, 255, 0.45);
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.7), 0 0 26px rgba(160, 75, 255, 0.16);
}
/* Hidden only once the script has said it is there to unhide them. A page that
   needs JavaScript to show its own words is not a page, and the book is read
   in whatever somebody happens to have open. */
.js .cel {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.45s ease, transform 0.5s cubic-bezier(0.2, 0.85, 0.3, 1);
}
.js .cel.in { opacity: 1; transform: none; }
.cel:hover { border-color: rgba(33, 243, 255, 0.5); }
/* A cel inside the margin is not on the twelve columns any more, and asking
   for all of them there makes its grid build eleven tracks nobody wanted. */
.margin > .cel, .record > .cel { grid-column: auto; }

/* The label of a cel, sitting on its top edge the way a comic captions a
   panel. The glyph does most of the work: a reader knows which cel this is
   before they have read the word. */
.tab {
  position: absolute;
  top: -0.66rem;
  left: 0.85rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.14rem 0.55rem;
  background: var(--void);
  border: 2px solid currentColor;
  color: var(--violet);
  font-size: 0.5rem;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  text-shadow: 0 0 10px currentColor;
  white-space: nowrap;
}
.ic {
  flex: none;
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tab .ic { width: 0.85rem; height: 0.85rem; }

/* Who flew it and when, signed into the bottom corner of the splash — where a
   comic signs a panel, and the one corner the sentence never reaches, because
   the sentence is capped at 17ch and pinned to the left. Stacked, so it reads
   down the corner as four short facts rather than across it as one long line
   arguing with the sentence for the same inch. */
.credits {
  position: absolute;
  z-index: 2;
  right: clamp(1.2rem, 3.6vw, 2.6rem);
  bottom: clamp(1.2rem, 3.6vw, 2.6rem);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.4rem;
  text-align: right;
}
.badge { display: flex; align-items: baseline; gap: 0.5rem; }
.badge b {
  font-size: 1.45rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--violet);
  text-shadow: 0 0 18px currentColor;
}
.badge i {
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--dim);
}
.ver {
  padding: 0.1rem 0.42rem;
  border: 1px solid rgba(33, 243, 255, 0.45);
  font-size: 0.56rem;
  letter-spacing: 0.2em;
  color: var(--cyan);
}
.credits .who {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.6rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cyan);
}
/* Whoever flew it, on their own chapter's splash — smaller than it was on a
   bar of its own, because the sentence underneath is the loud thing here. */
.credits .who .face {
  width: 2.3rem;
  height: 2.3rem;
  border-color: rgba(33, 243, 255, 0.55);
  box-shadow: 0 0 12px rgba(33, 243, 255, 0.35);
}
.credits .when {
  font-size: 0.56rem;
  letter-spacing: 0.14em;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* --- the splash -----------------------------------------------------------
   One version is one sentence, and this is where it gets said at the size it
   deserves. The plate goes behind it when the chapter has one — painted once
   by tools/chronicle-art.sh and kept in docs/art/ from then on — and the
   halftone dots are there either way, so a chapter with no plate reads as a
   panel somebody meant rather than as one short of a picture. */
.splash {
  min-height: clamp(14rem, 42vh, 24rem);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  padding: clamp(1.2rem, 3.6vw, 2.6rem);
  border-color: rgba(255, 62, 200, 0.4);
  background: radial-gradient(120% 100% at 82% 4%, rgba(160, 75, 255, 0.3), transparent 62%), #05010c;
}
.plate-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.62;
  /* the book is a CRT and the plate did not come off one, so it is sent
     through the same glass as everything else on the page */
  filter: saturate(1.18) contrast(1.06);
  animation: breathe 34s ease-in-out infinite alternate;
}
@keyframes breathe {
  from { transform: scale(1.03); }
  to { transform: scale(1.14) translate(-1.6%, -1.4%); }
}
/* Halftone. A comic is dots before it is anything else, and these ones drift,
   which is the cheapest way there is to make a still page look like it runs. */
.dots {
  position: absolute;
  inset: -12%;
  background-image: radial-gradient(rgba(255, 62, 200, 0.55) 1.1px, transparent 1.3px);
  background-size: 7px 7px;
  opacity: 0.2;
  animation: halftone 18s linear infinite;
}
@keyframes halftone { to { transform: translate(7px, 7px); } }
/* the scanline layer sits behind main, so the splash gets its own, along with
   the scrim that keeps the tagline legible over whatever was painted */
.splash::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to top, rgba(5, 1, 12, 0.95) 4%, rgba(5, 1, 12, 0.4) 52%, rgba(5, 1, 12, 0.15)),
    repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.3) 0 1px, transparent 1px 3px);
}
.say { position: relative; z-index: 2; }
.kicker {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-bottom: 0.7rem;
  font-size: 0.54rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--lime);
  text-shadow: 0 0 14px currentColor;
}
/* What happened to the game, in letters you can read from the other side of
   the room. Every version has one, and on a page it is the whole headline. */
.shout {
  font-size: clamp(1.75rem, 5.4vw, 4.2rem);
  font-weight: 600;
  line-height: 1.04;
  /* the cover title is spaced out like a marquee; a sentence is not */
  letter-spacing: 0;
  margin-right: 0;
  max-width: 17ch;
  text-wrap: balance;
  background: linear-gradient(100deg,
    #ff3ec8, #ffb020, #b6ff3d, #21f3ff, #a04bff, #ff3ec8);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 3px 0 rgba(0, 0, 0, 0.85)) drop-shadow(0 0 24px rgba(255, 62, 200, 0.35));
  animation: slide 9s linear infinite;
}
.plate-full {
  position: absolute;
  z-index: 2;
  top: 0.7rem;
  right: 0.75rem;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.24rem 0.5rem;
  background: rgba(7, 3, 15, 0.75);
  border: 1px solid rgba(33, 243, 255, 0.45);
  color: var(--cyan);
  font-size: 0.5rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  text-decoration: none;
}
.plate-full:hover { border-color: var(--cyan); box-shadow: 0 0 16px rgba(33, 243, 255, 0.3); }
/* with the script running the link does not go anywhere, so it says so */
.js .plate-full { cursor: zoom-in; }

/* --- the plate, in full ---------------------------------------------------
   The link under the splash still points at the picture, and with the script
   off the browser does the old obvious thing with it. With the script on the
   chapter stays where it is and the plate climbs out of the panel it was
   behind instead — uncropped, over the page it belongs to, and gone again on
   a click, on Escape, or on the button in the corner. */
.lit {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(0.7rem, 2vh, 1.2rem);
  padding: clamp(1rem, 4vw, 3rem);
  cursor: zoom-out;
  background: radial-gradient(120% 90% at 50% 45%, rgba(20, 4, 40, 0.86), rgba(3, 1, 8, 0.97));
  opacity: 0;
  transition: opacity 420ms ease;
}
.lit.on { opacity: 1; }
/* the book is a CRT, and a picture held up in front of it is behind the same
   glass as everything else on the page */
.lit::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.32) 0 1px, transparent 1px 3px);
}
.lit img {
  max-width: 100%;
  max-height: 80vh;
  object-fit: contain;
  border: 1px solid rgba(255, 62, 200, 0.45);
  box-shadow: 0 0 70px rgba(160, 75, 255, 0.35);
  /* the same glass the plate wears in the panel it came out of */
  filter: saturate(1.18) contrast(1.06);
}
.lit-cap {
  position: relative;
  max-width: 46rem;
  text-align: center;
  color: var(--dim);
  font-size: 0.62rem;
  line-height: 1.8;
  letter-spacing: 0.14em;
  text-wrap: balance;
}
.lit-cap:empty { display: none; }
/* the caption and the way out arrive after the plate has landed, so the first
   half-second is the picture and nothing else */
.lit-cap, .lit-x {
  opacity: 0;
  transform: translateY(0.4rem);
  transition: opacity 260ms ease, transform 260ms ease;
}
.lit.on .lit-cap, .lit.on .lit-x {
  opacity: 1;
  transform: none;
  transition-delay: 300ms;
}
.lit-x {
  position: absolute;
  top: clamp(0.6rem, 2vw, 1.3rem);
  right: clamp(0.6rem, 2vw, 1.3rem);
  width: 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  padding: 0;
  background: rgba(7, 3, 15, 0.75);
  border: 1px solid rgba(33, 243, 255, 0.45);
  color: var(--cyan);
  font: inherit;
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.lit-x:hover { border-color: var(--cyan); box-shadow: 0 0 16px rgba(33, 243, 255, 0.3); }
/* the page underneath does not scroll while the plate is up, or the zoom it
   flies back into would have moved by the time it got there */
.lit-open, .lit-open body { overflow: hidden; }

/* --- the narration --------------------------------------------------------
   The pilot's own sentence about what they did, in what a comic would call a
   caption box. Capped at 600px, because that is where a line of monospace
   stops being comfortable — and that cap is what makes the rest of the page
   divide into cels in the first place. */
.told { grid-column: span 5; }
.deed {
  max-width: 600px;
  font-size: clamp(0.92rem, 1.3vw, 1.12rem);
  line-height: 1.6;
  color: var(--ink);
  text-wrap: pretty;
}
.deed::first-letter {
  float: left;
  padding: 0.04em 0.09em 0 0;
  font-size: 2.6em;
  line-height: 0.82;
  color: var(--amber);
  text-shadow: 0 0 20px rgba(255, 176, 32, 0.5);
}

/* --- the numbers ----------------------------------------------------------
   Six tiles, each with its glyph, each counting up as the cel lands. They are
   the same six facts a chapter always carried; they have just stopped being a
   footnote about themselves. */
.figures { grid-column: span 7; }
.stats {
  list-style: none;
  display: grid;
  /* three and three, so the six of them read as a block rather than as a row
     that ran out */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: minmax(3.2rem, 1fr);
  gap: 0.45rem;
}
/* The six of them fill the cel, so the numbers stand level with the narration
   in the cel beside them rather than bunching at the top of a tall panel. Only
   here: the ledger uses the same tiles with a paragraph and a list of receipts
   underneath, and a list told to be as tall as its parent takes the whole cel
   and leaves everything after it standing outside the border. */
.figures .stats { height: 100%; }
.stats li {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.6rem;
  align-content: center;
  padding: 0.5rem 0.6rem;
  background: rgba(160, 75, 255, 0.09);
  border: 1px solid rgba(160, 75, 255, 0.28);
  border-left: 3px solid var(--cyan);
  transition: background 0.2s, border-left-color 0.2s;
}
.stats li:hover { background: rgba(255, 62, 200, 0.11); border-left-color: var(--magenta); }
.stats .ic {
  grid-row: 1 / 3;
  align-self: center;
  width: 1.7rem;
  height: 1.7rem;
  color: var(--cyan);
  opacity: 0.8;
}
.stats li:hover .ic { color: var(--magenta); opacity: 1; }
.stats b {
  font-size: 1.3rem;
  font-weight: 500;
  line-height: 1.15;
  color: var(--cyan);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 16px rgba(33, 243, 255, 0.35);
}
.stats span {
  font-size: 0.48rem;
  line-height: 1.4;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dim);
}

/* The commit, drawn: one shape per sector it touched, drawn as the thing it
   is — an event is a mine, the rules are a gear, the song is a note, a panel is
   a panel — sized by how much of that sector moved and coloured by which part
   of the cabinet it was. The diff picks the pilot's move: wreckage is shot
   down and sheds shards, a new arrival rides in on a tow beam and rings while
   it settles, a tuning pass gets a reticle where a shot would be, and
   anything else is the standing order — fire at the biggest thing you moved.
   None of it is random — tools/chronicle.sh seeds it with the commit hash, so
   a version looks the same in every clone forever. */
/* Seven columns rather than twelve: the field is drawn on a 1400x640 board, so
   at full width it turns into a wall of empty space with three rocks in it.
   Beside the margin it is a picture. With nothing in the margin it takes the
   whole row back. */
/* align-self, so a version with five things in its margin does not stretch the
   field into a black rectangle with one rock adrift in it. The picture keeps
   the height its own shape asks for and the margin runs on past it. */
.frame {
  grid-column: span 7;
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding-bottom: 0.8rem;
}
.frame .art { display: block; width: 100%; height: auto; }
.ch:not(.has-record):not(.has-aside) .frame,
.ch.long-aside .frame { grid-column: span 12; }
.ch:not(.has-record):not(.has-aside) .frame .art,
.ch.long-aside .frame .art { max-height: 52vh; }
.frame figcaption {
  font-size: 0.55rem;
  letter-spacing: 0.1em;
  color: var(--dim);
  text-align: center;
}
.art .star {
  fill: var(--ink);
  opacity: 0.5;
  animation: twinkle 3.4s ease-in-out infinite alternate;
  animation-delay: var(--d);
}
.art .drift {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: drift 14s ease-in-out infinite alternate;
  animation-delay: var(--d);
}
.art .rock {
  fill: rgba(7, 3, 15, 0.5);
  stroke: var(--c);
  stroke-width: 2.2;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: spin var(--s) linear infinite;
  filter: drop-shadow(0 0 6px var(--c));
}
.art .rock.gone { fill: none; stroke-dasharray: 5 8; opacity: 0.4; filter: none; }
/* The rest of the field's vocabulary. A glyph is a sector drawn as what it
   is; its stroke arrives pre-divided by the group's scale so every silhouette
   carries the same line the rocks do. "solid" blots out the stars behind a
   body, "pip" is the light a thing keeps on, "whirl" turns because a gear
   that does not is a decal. */
.art .glyph {
  fill: none;
  stroke: var(--c);
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 6px var(--c));
}
.art .glyph .solid { fill: rgba(7, 3, 15, 0.5); }
.art .glyph .pip { fill: var(--c); stroke: none; animation: pip 1.6s ease-in-out infinite alternate; }
.art .glyph .spark { animation: pip 0.9s ease-in-out infinite alternate; }
.art .glyph .scan { opacity: 0.55; }
.art .glyph .whirl {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: spin 26s linear infinite;
}
.art .glyph.gone { opacity: 0.4; filter: none; }
.art .glyph.gone .solid { fill: none; }
.art .glyph.gone .pip { animation: none; opacity: 0.5; }
/* Dead machinery stops. A wreck that keeps turning is not a wreck. */
.art .glyph.gone .whirl, .art .glyph.gone .spark { animation: none; }
/* What the diff did, marked on the thing it did it to: a new arrival rings
   while it settles, wreckage sheds shards on their own way out. */
.art .born {
  fill: none;
  stroke: var(--c);
  stroke-width: 1.6;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: bornring 2.8s linear infinite;
  animation-delay: var(--d);
}
.art .shard {
  fill: none;
  stroke: var(--c);
  stroke-width: 1.6;
  transform-box: fill-box;
  animation: shardfly 3.2s ease-out infinite;
  animation-delay: var(--d);
}
/* The pilot's move, when it is not the standing shot: the quiet aim line and
   slow reticle of a tuning pass, the tow beam hauling a new arrival in, the
   burst where a kill connected. */
.art .aim { stroke: var(--lime); stroke-width: 1.6; opacity: 0.25; }
.art .ret {
  fill: none;
  stroke: var(--lime);
  stroke-width: 2;
  opacity: 0.8;
  filter: drop-shadow(0 0 6px var(--lime));
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: spin 40s linear infinite;
}
.art .beam {
  stroke: var(--cyan);
  stroke-width: 2;
  stroke-dasharray: 5 11;
  opacity: 0.6;
  animation: march 1.4s linear infinite;
}
.art .hit {
  stroke: var(--lime);
  stroke-width: 2.4;
  filter: drop-shadow(0 0 6px var(--lime));
  animation: pip 0.8s ease-in-out infinite alternate;
}
.art .ship {
  fill: none;
  stroke: var(--lime);
  stroke-width: 2.6;
  filter: drop-shadow(0 0 8px var(--lime));
}
.art .shot {
  stroke: var(--lime);
  stroke-width: 2;
  stroke-dasharray: 9 15;
  opacity: 0.55;
  animation: march 1.1s linear infinite;
}
/* A file that came back lighter than it went has had a piece taken out of it.
   A rock loses it out of the outline itself, because a rock is drawn fresh
   every time and can be; a silhouette is fixed markup and cannot, so it loses
   it beside itself instead. Either way the chip stays where it came off - a
   shard flies away and means the thing is gone, and this one is not. */
.art .crumb {
  fill: none;
  stroke: var(--c);
  stroke-width: 1.6;
  stroke-linejoin: round;
  opacity: 0.55;
  filter: drop-shadow(0 0 4px var(--c));
}
/* And what the words said, when the tagline said something a picture can hold:
   speed lines off whatever the pilot was aiming at, or two more of the thing
   when the line says there was more than one of them. Garnish rather than
   grammar - it fires on about one chapter in twenty-six, and that is the
   intended rate. */
.art .streak {
  stroke: var(--lime);
  stroke-width: 2.4;
  stroke-linecap: round;
  opacity: 0.5;
  filter: drop-shadow(0 0 6px var(--lime));
  animation: streak 1.1s ease-out infinite;
  animation-delay: var(--d);
}
.art .echo {
  fill: none;
  stroke: var(--c);
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.3;
  animation: echopulse 3.6s ease-in-out infinite alternate;
  animation-delay: var(--d);
}
.art .ghost { font: 700 400px var(--mono); fill: var(--violet); opacity: 0.07; }
.art .stamp rect { fill: none; stroke: currentColor; stroke-width: 3; opacity: 0.75; }
.art .stamp text {
  fill: currentColor;
  font: 600 40px var(--mono);
  letter-spacing: 0.18em;
  text-anchor: middle;
}
.art .stamp.off { color: var(--magenta); }
.art .stamp.over { color: var(--amber); }
.art .stamp.untold { color: var(--dim); opacity: 0.6; }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes drift { to { transform: translate(14px, -11px); } }
@keyframes twinkle { to { opacity: 0.12; } }
@keyframes march { to { stroke-dashoffset: -48; } }
@keyframes pip { to { opacity: 0.25; } }
@keyframes streak { from { opacity: 0.7; } to { opacity: 0; } }
@keyframes echopulse { to { opacity: 0.1; } }
@keyframes bornring {
  0% { transform: scale(0.55); opacity: 0.9; }
  100% { transform: scale(1.5); opacity: 0; }
}
@keyframes shardfly {
  0% { transform: translate(0, 0); opacity: 0.9; }
  100% { transform: translate(var(--tx), var(--ty)); opacity: 0; }
}

/* --- what the book had to write down --------------------------------------
   The loud cels. A rule bent in writing, a rule changed outright, or a commit
   the referee never got to look at — each with the rule itself in letters that
   carry, because the whole point of a budget is that spending it is visible
   from the doorway. */
/* The margin runs down the side of the drawn field and holds everything that
   happened around this version rather than in it. */
.margin {
  grid-column: span 5;
  display: grid;
  gap: clamp(0.6rem, 1.1vw, 1rem);
}
/* A version that had three other things happen while it was on the cabinet has
   more margin than there is margin. It comes out from beside the field and
   runs underneath it in columns, where the notes read as a stack of cards
   rather than as one long wall down the side. */
.ch.long-aside .margin { grid-column: span 12; }
.ch.long-aside .meanwhile {
  columns: 22rem;
  column-gap: clamp(0.8rem, 1.6vw, 1.6rem);
}
.ch.long-aside .interlude { break-inside: avoid; margin: 0 0 0.9rem; }
.record {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: clamp(0.6rem, 1.1vw, 1rem);
}
.loud { color: var(--amber); }
.loud .tab { color: inherit; }
/* The burst a comic puts behind a noise, turning slowly enough to be noticed
   rather than watched. Masked rather than clipped, because clipping the cel
   would take the corner off its own label with it. */
.loud::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 9rem;
  height: 9rem;
  background: conic-gradient(from 0deg, currentColor 0 5deg, transparent 5deg 30deg);
  -webkit-mask-image: radial-gradient(closest-side, #000 28%, transparent 76%);
  mask-image: radial-gradient(closest-side, #000 28%, transparent 76%);
  opacity: 0.16;
  pointer-events: none;
  animation: spin 70s linear infinite;
}
.klaxon {
  position: relative;
  font-size: clamp(1.7rem, 3.6vw, 2.9rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.04em;
  text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.8), 0 0 28px currentColor;
}
.reason {
  max-width: 600px;
  margin-top: 0.55rem;
  font-size: 0.7rem;
  line-height: 1.75;
  color: var(--ink);
}
.byline {
  margin-top: 0.5rem;
  font-size: 0.52rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
}
.loud.over { border-color: rgba(255, 176, 32, 0.6); }
.loud.law { color: var(--violet); border-color: rgba(160, 75, 255, 0.65); }
/* The other two are a pilot saying what they did. This one is the book saying
   it for them, because nobody wrote it down and the referee was not running.
   It is the one cel on the page that flashes, and it flashes on purpose. */
.loud.off {
  color: var(--magenta);
  border-color: var(--magenta);
  background: linear-gradient(168deg, rgba(255, 62, 200, 0.16), rgba(7, 3, 15, 0.88) 52%);
  animation: alarm 2.6s ease-in-out infinite;
}
@keyframes alarm {
  50% { box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.7), 0 0 40px rgba(255, 62, 200, 0.45); }
}

/* --- the dock ---------------------------------------------------------------
   Forward, back, and out — bolted to the bottom of the window instead of the
   bottom of the page, because a reader halfway down a chapter is exactly the
   reader most likely to want the next one. The arrow keys do the same thing;
   see the script at the foot of every page. */
.dock {
  position: fixed;
  z-index: 5;
  inset: auto 0 0 0;
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: stretch;
  gap: clamp(0.3rem, 1vw, 0.75rem);
  padding: 0.45rem clamp(0.45rem, 1.8vw, 1.2rem);
  background: linear-gradient(to top, rgba(7, 3, 15, 0.98), rgba(9, 4, 20, 0.9));
  border-top: 1px solid rgba(160, 75, 255, 0.5);
  box-shadow: 0 -12px 34px rgba(7, 3, 15, 0.85);
  backdrop-filter: blur(6px);
}

/* The two a reader actually wants, at the size of something you press. */
.turn {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.75rem;
  border: 1px solid rgba(182, 255, 61, 0.45);
  background: rgba(182, 255, 61, 0.06);
  color: var(--lime);
  text-decoration: none;
  white-space: nowrap;
}
.turn:hover {
  color: var(--ink);
  border-color: var(--lime);
  background: rgba(182, 255, 61, 0.16);
  box-shadow: 0 0 18px rgba(182, 255, 61, 0.25);
}
.turn .arw { font-size: 1.1rem; line-height: 1; text-shadow: 0 0 12px currentColor; }
.turn .lab { display: grid; gap: 0.1rem; text-align: left; }
.turn.next .lab { text-align: right; }
.turn .lab i {
  font-style: normal;
  font-size: 0.46rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--dim);
}
.turn .lab b { font-size: 0.78rem; font-weight: 500; letter-spacing: 0.1em; }
/* The first chapter and the one on the cabinet each have one edge with nothing
   past it. The button stays where it was, unlit, so the row does not shuffle. */
.turn.none {
  border-color: rgba(154, 134, 189, 0.22);
  background: none;
  color: rgba(154, 134, 189, 0.45);
}
.turn.none .lab b { font-size: 0.6rem; letter-spacing: 0.16em; }
.up {
  display: grid;
  place-items: center;
  gap: 0.14rem;
  padding: 0 0.6rem;
  border: 1px solid rgba(160, 75, 255, 0.4);
  color: var(--violet);
  text-decoration: none;
}
.up .ic { width: 0.95rem; height: 0.95rem; }
.up i { font-style: normal; font-size: 0.44rem; letter-spacing: 0.22em; text-transform: uppercase; }
.up:hover { color: var(--ink); border-color: var(--cyan); }

/* Every book there has ever been, oldest first, always in reach. It used to be
   every version, and by about thirty of them the row had stopped being a shelf
   and started being a filmstrip nobody could aim at. A book is a run of
   chapters one pilot flew with nobody else landing in between, which is a
   coarser thing to press and a truer thing to name — and the chapters are one
   press away rather than none, which is the trade.

   More books than window and this scrolls sideways — which it will, and which
   is why it is the only part of the dock allowed to move. */
.rail {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  justify-content: safe center;
  scrollbar-width: thin;
  scrollbar-color: rgba(160, 75, 255, 0.5) transparent;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 1.2rem, #000 calc(100% - 1.2rem), transparent);
  mask-image: linear-gradient(to right, transparent, #000 1.2rem, #000 calc(100% - 1.2rem), transparent);
}
.rail::-webkit-scrollbar { height: 3px; }
.rail::-webkit-scrollbar-thumb { background: rgba(160, 75, 255, 0.5); }
/* One spine per book. The numeral is what it is called, the range is what is
   inside it, and the face is whose run it was — which is the fact the shelf
   exists to show, and the one the old row of numbered thumbnails never did. */
.spine {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  height: 2.4rem;
  padding: 0 0.5rem;
  border: 1px solid rgba(160, 75, 255, 0.3);
  background: linear-gradient(to top, rgba(160, 75, 255, 0.16), rgba(11, 4, 24, 0.9));
  color: var(--dim);
  font: inherit;
  cursor: pointer;
}
.spine .face {
  width: 1.6rem;
  height: 1.6rem;
  border-color: rgba(160, 75, 255, 0.5);
  box-shadow: none;
}
.spine .sp { display: grid; gap: 0.02rem; text-align: left; }
.spine .sp b {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--violet);
}
.spine .sp i {
  font-style: normal;
  font-size: 0.46rem;
  letter-spacing: 0.1em;
  font-variant-numeric: tabular-nums;
}
.spine .nm {
  max-width: 7rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.5rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
/* Every mark its chapters wear, once each. On a spine this is the only thing
   that says what the run was about, so it sits at the end where the eye lands
   after the numeral — and it is the reason a shelf beats a row of numbers. */
.spine .marks { flex-wrap: nowrap; margin-left: 0.1rem; }
.spine:hover { border-color: var(--cyan); color: var(--ink); }
.spine.here { border-color: var(--cyan); box-shadow: 0 0 0 1px var(--cyan), 0 0 16px rgba(33, 243, 255, 0.3); }
.spine.here .sp b { color: var(--cyan); }
.spine[aria-expanded="true"] {
  border-color: var(--amber);
  color: var(--ink);
  background: linear-gradient(to top, rgba(255, 176, 32, 0.22), rgba(11, 4, 24, 0.9));
}
.spine[aria-expanded="true"] .sp b { color: var(--amber); }

/* The book, pulled off the shelf. It stacks above the dock rather than
   replacing it, so the shelf you pulled it from is still under your thumb —
   and nothing has been navigated yet, which is the whole point: a book opening
   is not a page turning. Press a chapter for that. */
.tray {
  position: absolute;
  inset: auto 0 100% 0;
  max-height: min(58vh, 30rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.6rem clamp(0.45rem, 1.8vw, 1.2rem) 0.7rem;
  background: linear-gradient(to top, rgba(9, 4, 20, 0.99), rgba(7, 3, 15, 0.97));
  border-top: 1px solid rgba(255, 176, 32, 0.5);
  box-shadow: 0 -18px 40px rgba(7, 3, 15, 0.9);
  backdrop-filter: blur(6px);
}
.tray-head {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  margin-bottom: 0.5rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px dashed rgba(255, 176, 32, 0.3);
  font-size: 0.54rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
}
.tray-head b { font-size: 0.7rem; font-weight: 600; color: var(--amber); }
.tray-head span { color: var(--ink); }
.tray-head i { font-style: normal; margin-left: auto; color: var(--cyan); font-variant-numeric: tabular-nums; }
/* Stacked, oldest at the top, the way the run was flown. */
.stack { display: grid; gap: 3px; }
.leaf {
  display: grid;
  grid-template-columns: 3.2rem 2.6rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.7rem;
  padding: 0.24rem 0.4rem 0.24rem 0.24rem;
  border: 1px solid rgba(160, 75, 255, 0.24);
  color: var(--dim);
  text-decoration: none;
}
.leaf .thumb {
  height: 2.1rem;
  overflow: hidden;
  /* the halftone a chapter with no plate falls back to, under the plate a
     chapter with one covers it with */
  background: radial-gradient(rgba(255, 62, 200, 0.5) 1px, transparent 1.2px) 0 0 / 6px 6px, #0b0418;
}
.leaf .thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.75;
  /* a plate is a square and a thumbnail is not, so it arrives cropped and
     often cropped to the darkest part of itself - lifted here, because a
     thumbnail nobody can make out is a blank tile with extra steps */
  filter: saturate(1.25) contrast(1.05) brightness(1.25);
  transition: opacity 0.15s, transform 0.35s;
}
.leaf b {
  font-size: 0.6rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  font-variant-numeric: tabular-nums;
}
.leaf .line {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--amber);
}
/* Hard against the right edge, so a stack of six chapters reads as a column of
   marks somebody can scan down rather than six ragged tails. */
.leaf .marks { flex-wrap: nowrap; justify-content: flex-end; }
.leaf:hover { border-color: var(--cyan); color: var(--ink); background: rgba(33, 243, 255, 0.08); }
.leaf:hover .thumb img { opacity: 1; transform: scale(1.06); }
.leaf.bent { border-color: rgba(255, 176, 32, 0.6); }
.leaf.bent b { color: var(--amber); }
.leaf.off { border-color: var(--magenta); }
.leaf.off b { color: var(--magenta); }
.leaf.here { border-color: var(--cyan); box-shadow: inset 0 0 0 1px rgba(33, 243, 255, 0.4); }
.leaf.here b { color: var(--cyan); }

/* A version whose pilot wrote no Chronicle line reads as a gap in the record,
   and looks like one, so the next person can see what a missing chapter costs. */
.deed.untold {
  color: var(--dim);
  font-size: 0.86rem;
  font-style: italic;
}
.deed.untold::first-letter { float: none; font-size: 1em; color: inherit; text-shadow: none; }
.subj {
  max-width: 600px;
  margin-top: 0.7rem;
  padding-top: 0.5rem;
  border-top: 1px dashed rgba(160, 75, 255, 0.3);
  font-size: 0.62rem;
  line-height: 1.6;
  letter-spacing: 0.12em;
  color: var(--dim);
}
/* Something happened that was not a version: the rules moved, the book was
   rebuilt, somebody tidied. It goes in the record because everything goes in
   the record, but it never took a number, so it does not take a page either —
   it sits in the margin of whichever version was on the cabinet at the time. */
.interlude {
  padding-left: 0.9rem;
  border-left: 2px dashed rgba(160, 75, 255, 0.3);
}
.interlude + .interlude { margin-top: 0.8rem; }
.between {
  max-width: 600px;
  font-size: 0.78rem;
  line-height: 1.7;
  letter-spacing: 0.04em;
  color: var(--ink);
}
.between b { color: var(--violet); font-weight: 500; }
.interlude .subj { margin-top: 0.35rem; padding-top: 0; border: none; font-size: 0.58rem; }

/* The four things the book records whether you like it or not, here in the
   margin where the interludes live. On the chapter itself they get a cel each. */
.override, .lawchange, .unrecorded, .ledger {
  max-width: 600px;
  margin-top: 0.6rem;
  padding: 0.45rem 0.7rem;
  font-size: 0.66rem;
  line-height: 1.65;
  border-left: 2px solid currentColor;
}
.override { color: var(--magenta); background: rgba(255, 62, 200, 0.07); }
.lawchange { color: var(--amber); background: rgba(255, 176, 32, 0.07); }
.ledger { color: var(--violet); background: rgba(160, 75, 255, 0.07); }

/* The other two are a pilot saying what they did. This one is the book saying
   it for them, because nobody wrote it down and the referee was not running.
   It reads at full brightness on purpose: the declared ones are colour on the
   page, this one is the page talking. */
.unrecorded {
  color: var(--ink);
  background: rgba(255, 62, 200, 0.16);
  border-left-color: var(--magenta);
  text-shadow: 0 0 12px rgba(255, 62, 200, 0.45);
}
.roster td:last-child b { color: var(--ink); font-weight: 500; }

.play {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.55rem;
  padding: 0.7rem 1rem;
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--dim);
}
.play .ic { width: 1.1rem; height: 1.1rem; color: var(--lime); }
.play code {
  color: var(--lime);
  background: rgba(182, 255, 61, 0.08);
  border: 1px solid rgba(182, 255, 61, 0.25);
  padding: 0.14rem 0.5rem;
  user-select: all;
}

/* --- the room ---------------------------------------------------------------
   The book has a soundtrack and it is on. docs/chronicle-song.js builds this
   switch itself rather than finding it printed in the page, so a reader with
   the script off is never offered a sound that cannot arrive — same bargain as
   everything else the script does. Top right, out of the way of the dock, and
   small enough that a reader who does not want it never thinks about it. */
.song {
  position: fixed;
  z-index: 6;
  top: clamp(0.4rem, 1.5vw, 0.9rem);
  right: clamp(0.4rem, 1.5vw, 0.9rem);
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.36rem 0.62rem;
  font-family: inherit;
  font-size: 0.48rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--dim);
  background: rgba(7, 3, 15, 0.72);
  border: 1px solid rgba(33, 243, 255, 0.24);
  cursor: pointer;
  backdrop-filter: blur(6px);
}
.song:hover { color: var(--ink); border-color: var(--cyan); }
.song.on {
  color: var(--cyan);
  border-color: rgba(33, 243, 255, 0.65);
  box-shadow: 0 0 18px rgba(33, 243, 255, 0.2);
}
/* Four bars, flat while it is silent, because a meter that moves with nothing
   coming out of the speakers is the kind of decoration this book does not do. */
.eq { display: flex; align-items: flex-end; gap: 2px; height: 0.68rem; }
.eq i { width: 2px; height: 20%; background: currentColor; }
.song.on .eq i { animation: eq 2.1s ease-in-out infinite; }
.song.on .eq i:nth-child(2) { animation-duration: 3.4s; animation-delay: -0.9s; }
.song.on .eq i:nth-child(3) { animation-duration: 2.7s; animation-delay: -1.6s; }
.song.on .eq i:nth-child(4) { animation-duration: 4.1s; animation-delay: -0.4s; }
/* Lit, and waiting for the browser to allow a noise at all. Three classes on
   purpose: this outranks the reduced-motion rule below, which parks the bars
   at the height that means sound is coming out. */
.song.on.waiting .eq i { animation: none; height: 20%; }
@keyframes eq {
  0%, 100% { height: 20%; }
  50% { height: 100%; }
}

/* Below the width where two cels side by side stop being two cels and start
   being two columns of four words, there is one column. */
/* --- the pilots -----------------------------------------------------------
   One page per person, built out of the same furniture as a chapter, because
   it is built out of the same stream: a splash with the face where the plate
   goes, cels, stats, a shelf. Only three things here are new — a bar per mark,
   a board of flights, and a dock that is a shelf of people rather than books. */

/* Every name the book prints is now a way into the rest of what that person
   did. It inherits its colour on purpose: a roster that turned every name cyan
   would read as a table of links rather than as a table of pilots. */
.pl { color: inherit; text-decoration: none; }
.pl:hover, .pl:focus-visible { color: var(--magenta); text-shadow: 0 0 12px currentColor; }
.roster .pl { display: flex; align-items: center; gap: 0.5rem; color: var(--ink); }
.tray-head .pl { color: var(--ink); }

/* A pilot page is eleven cels of nine different shapes, where a chapter is
   five of three — so the row gap a chapter reads well at leaves this one
   looking like one long panel with lines drawn across it. The label of a cel
   sits half out of its own top edge, and that half needs somewhere to be: the
   gap here is the tab's height plus room around it, so no heading is ever
   reading as the last line of the cel above it. */
.page main.who {
  row-gap: clamp(1.9rem, 3.4vh, 2.9rem);
  column-gap: clamp(0.7rem, 1.3vw, 1.15rem);
  padding-top: clamp(1.2rem, 3vh, 2rem);
}
/* The same reason, one level down: every cel here opens with a heading that is
   sitting on the border, so the first thing inside it starts below where that
   heading ends rather than beside it. */
.ch.who > .cel { padding-top: 1.6rem; }
.ch.who .gist { margin-top: 0.2rem; }

/* What they move and what it cost them, side by side and deliberately not the
   same width: nine bars need a bar's worth of room to be read across, three
   tiles and a list of receipts do not. Either one turns up without the other
   often enough — a pilot with a blank ledger, a pilot who has only ever
   mentioned — and then it takes the whole twelve, because half a row with
   nothing beside it is a gap rather than a column. */
.halfwide { grid-column: span 7; }
.halfnarrow { grid-column: span 5; }

/* The face at the size it was painted. Every other page in this book shows it
   at the size of a thumbnail, and a portrait stretched across a splash the way
   a plate is would be a band of colour rather than a person - so this one is
   framed beside the name instead of laid behind it. */
.whois { min-height: clamp(13rem, 30vh, 19rem); }
.whois .say { display: flex; align-items: center; gap: clamp(1rem, 3vw, 2rem); }
.portrait {
  flex: none;
  width: clamp(5rem, 12vw, 8.5rem);
  height: clamp(5rem, 12vw, 8.5rem);
  border-radius: 50%;
  object-fit: cover;
  background: #05010c;
  border: 1px solid rgba(255, 62, 200, 0.6);
  box-shadow: 0 0 30px rgba(255, 62, 200, 0.35), inset 0 0 20px rgba(5, 1, 12, 0.8);
  filter: saturate(1.18) contrast(1.06);
}
.nameplate { min-width: 0; }
.ch.who .credits .badge b { color: var(--amber); }

/* The characterisation, and the badges under it. The paragraph is the one
   place in the book that has an opinion about a person rather than a count of
   them, so it is set at reading size instead of at caption size — and the
   badges are the same pills the marks wear everywhere else, because a reader
   who has learnt one row of them has learnt this one. */
/* A chapter puts one sentence beside six numbers, so the numbers get the wide
   half. This is a paragraph, and a paragraph wants the wide half instead - so
   the two swap over here, and the tiles go two across and three down rather
   than shrinking to a narrower version of the same shape. */
.ch.who .told { grid-column: span 7; }
.ch.who .figures { grid-column: span 5; }
.ch.who .figures .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ch.who .deed { font-size: clamp(0.8rem, 1.05vw, 0.95rem); line-height: 1.75; }
.ch.who .deed b { color: var(--cyan); font-weight: 500; }
/* The drop cap belongs to a chapter, where the deed is one sentence under a
   plate and the letter is the way in. Six sentences in a five-column cel is a
   paragraph, and a paragraph with a capital the height of four lines in it is
   a poster. */
.ch.who .deed::first-letter { float: none; font-size: inherit; color: inherit;
  padding: 0; line-height: inherit; text-shadow: none; }
.traits { margin-top: 1.15rem; }

/* What somebody keeps moving, as nine bars rather than nine numbers. Each bar
   is measured against that pilot's own busiest mark rather than against
   anybody else's, because this says what kind of pilot they are and not how
   they rank. The colours are the mark colours — .mk carries them, so the pill
   styling is what gets undone here and the palette is not restated. */
.prints { list-style: none; display: grid; gap: 0.3rem; margin-top: 0.9rem; }
.prints .mk {
  display: grid;
  grid-template-columns: 0.9rem minmax(5.5rem, 8rem) minmax(0, 1fr) 2rem;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.3rem 0.45rem;
  border: 0;
  border-left: 2px solid currentColor;
  border-radius: 0;
  background: rgba(5, 1, 12, 0.4);
}
.prints .mk em { letter-spacing: 0.16em; }
.prints .bar { display: block; height: 0.42rem; background: rgba(160, 75, 255, 0.16); }
.prints .bar i { display: block; height: 100%; background: currentColor; opacity: 0.75; }
.prints b {
  font-size: 0.7rem;
  text-align: right;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* What a player actually meets: the field-guide tiles that came out of a file
   this pilot created. A file count is a fact about a repository; this is the
   half of somebody that is still being played tonight.

   Three across, because a tile is a card rather than a row: the name, where
   the guide files it, the sentence a player reads and the file it came out of,
   stacked. A single column of these was a list of filenames with headings, and
   sixteen of them was most of the page. */
.tiles {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.55rem;
  margin-top: 1rem;
}
/* A column rather than fixed rows, because a tile that never wrote a briefing
   is three children and one that did is four - and the path goes to the foot
   of the card either way. */
.tiles li {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.34rem;
  padding: 0.7rem 0.75rem 0.65rem;
  border: 1px solid rgba(33, 243, 255, 0.22);
  border-left: 2px solid var(--cyan);
  background: rgba(33, 243, 255, 0.05);
}
.tiles li:hover { background: rgba(33, 243, 255, 0.1); border-color: rgba(33, 243, 255, 0.45); }
.tiles b { font-size: 0.74rem; letter-spacing: 0.16em; color: var(--cyan); }
.tiles span {
  font-size: 0.5rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--dim);
  justify-self: start;
}
/* The briefing, cut short by briefly() rather than by a line clamp: a sentence
   that stops at a word somebody chose reads better than one a box cut off. */
.tiles em {
  font-style: normal;
  font-size: 0.6rem;
  line-height: 1.65;
  color: var(--ink);
  text-wrap: pretty;
}
.tiles i {
  margin-top: auto;
  padding-top: 0.15rem;
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.06em;
  color: var(--dim);
  opacity: 0.75;
  overflow-wrap: anywhere;
}

/* The ambush they armed the room with. GR11 has no override on it, so this is
   the one section on the page that is about a rule rather than about a record. */
.traps { list-style: none; display: grid; gap: 0.5rem; margin-top: 0.9rem; }
.traps li {
  display: grid;
  gap: 0.24rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid rgba(255, 62, 200, 0.35);
  background: rgba(255, 62, 200, 0.06);
}
.traps b { font-size: 0.8rem; letter-spacing: 0.2em; color: var(--magenta); }
.traps span { font-size: 0.6rem; letter-spacing: 0.12em; color: var(--ink); }
.traps i {
  font-style: normal;
  font-size: 0.54rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}

/* Their flights, straight off the board, and the meter underneath read from
   tools/flights.sh rather than counted here. */
.board { width: 100%; border-collapse: collapse; margin-top: 0.9rem; font-size: 0.62rem; }
.board th {
  text-align: left;
  font-weight: 400;
  letter-spacing: 0.18em;
  color: var(--dim);
  padding: 0.3rem 0.4rem;
}
.board td {
  padding: 0.42rem 0.4rem;
  border-top: 1px solid rgba(160, 75, 255, 0.18);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  vertical-align: top;
}
.board td:nth-child(2) { color: var(--amber); }
.board td:last-child { color: var(--dim); letter-spacing: 0.02em; }
.meter {
  margin-top: 0.8rem;
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  color: var(--dim);
}
.meter b { color: var(--amber); }

/* The receipts under the ledger. The count is tools/tally.sh's answer and sits
   in the tiles above; these are the chapters where the reasons are, because a
   page that printed the number and hid the reasons would be worse than one
   that printed neither. */
.receipts { list-style: none; display: grid; gap: 3px; margin-top: 0.9rem; }
.receipts a, .receipts .norec {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.42rem 0.55rem;
  background: rgba(255, 176, 32, 0.07);
  border-left: 2px solid var(--amber);
  color: var(--ink);
  text-decoration: none;
}
.receipts a:hover { background: rgba(255, 176, 32, 0.16); }
.receipts b { color: var(--amber); font-size: 0.66rem; letter-spacing: 0.1em; }
.receipts span { font-size: 0.6rem; color: var(--ink); }
.receipts i {
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--dim);
  white-space: nowrap;
}

/* The cabinet is shared, and this is the only place on the page that says so. */
.visits { list-style: none; display: grid; gap: 0.35rem; margin-top: 0.9rem; }
.visits li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.7rem;
  padding: 0.42rem 0.55rem;
  border-left: 2px solid var(--violet);
  background: rgba(160, 75, 255, 0.08);
}
.visits .pl { font-size: 0.66rem; letter-spacing: 0.14em; color: var(--ink); }
/* A name in running prose brings its face with it, and there it has to be the
   size of the words rather than the size of a portrait. */
.gist .pl, .visits .pl { display: inline-flex; align-items: center; gap: 0.3rem; }
.gist .pl .face, .visits .pl .face { width: 1.3rem; height: 1.3rem; }
.visits span { font-size: 0.56rem; letter-spacing: 0.08em; color: var(--dim); }

/* A run of Chronicle lines is a portrait nobody sat for. */
.quotes { list-style: none; display: grid; gap: 0.5rem; margin-top: 0.9rem; }
.quotes li { display: grid; grid-template-columns: 2.6rem minmax(0, 1fr); gap: 0.7rem; align-items: baseline; }
.quotes a { color: var(--cyan); text-decoration: none; font-size: 0.66rem; letter-spacing: 0.1em; }
.quotes q { font-size: 0.68rem; line-height: 1.6; color: var(--ink); quotes: "\201C" "\201D"; }

/* The dock, and on a pilot page the shelf in it is a shelf of people. Same
   furniture, same corner of the window, one press to anybody else the cabinet
   has ever heard of. */
.whodock .spine { min-width: 8.5rem; }
.whodock .spine .nm {
  max-width: none;
  font-size: 0.56rem;
  letter-spacing: 0.14em;
  color: var(--ink);
}
.whodock .spine .sp i {
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.16em;
  color: var(--cyan);
  font-variant-numeric: tabular-nums;
}
.whodock .spine[aria-current="page"] { border-color: var(--magenta); }

@media (max-width: 62rem) {
  .told, .figures, .frame, .margin { grid-column: span 12; }
  /* said again at the specificity the pilot page swapped them over at, because
     a media query does not outrank a longer selector */
  .ch.who .told, .ch.who .figures { grid-column: span 12; }
  /* the pair stops being a pair before either half gets narrow enough that a
     nine-letter mark and its bar are fighting over the same inch */
  .halfwide, .halfnarrow { grid-column: span 12; }
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shout { max-width: none; }
  /* the sentence takes the whole width down here, so the corner it was
     keeping clear is gone: the credits fall back under it, still to the right */
  .credits {
    position: static;
    margin-top: 0.9rem;
  }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Narrow enough that the words on the two buttons are competing with the
   thumbnails for the same inch, and the arrows say it on their own. */
@media (max-width: 46rem) {
  .turn { padding: 0.3rem 0.5rem; }
  .turn .lab i { display: none; }
  .turn .lab b { font-size: 0.66rem; }
  .turn.none .lab { display: none; }
  .up i { display: none; }
  .up { padding: 0 0.45rem; }
  /* the face and the numeral say whose book it is; the name spelled out is
     the first thing that can go */
  .spine .nm { display: none; }
  /* except in the roster, where it is the only thing that says anything. A
     book has a numeral and a plate; a pilot has a portrait that was painted
     from a description and a name that is the whole of who they are, so the
     dock on a pilot page keeps the words and loses the picture instead. */
  .whodock .spine .nm { display: block; }
  .whodock .spine .face { display: none; }
  .whodock .spine { min-width: 0; }
}
@media (max-width: 34rem) {
  /* the bars say it on their own, the same way the arrows do */
  .song .lab { display: none; }
  .song { padding: 0.4rem 0.5rem; }
  .roster table { font-size: 0.62rem; }
  /* The marks stop being a column and become a row of their own, under the
     words they are about. A column of icons costs a quarter of the width here,
     and it takes it off the one thing on the row anybody is reading - which is
     how a four-word tagline ended up set over three lines beside two pictures. */
  .cv a { grid-template-columns: 2.9rem minmax(0, 1fr); gap: 0.35rem 0.5rem; }
  .cv .marks { grid-column: 2; justify-content: flex-start; }
  .ci { padding-left: 0.3rem; grid-template-columns: minmax(0, 1fr); }
  .ci .marks { grid-column: 1; grid-row: auto; justify-content: flex-start; }
  .book > summary { gap: 0.4rem 0.6rem; padding: 0.6rem; }
  .by .face { width: 1.7rem; height: 1.7rem; }
  .gist, .book .contents { margin-inline: 0.6rem; padding-inline: 0; }
  /* padding-inline: 0 above takes the words up against the rule in the margin,
     and the rule is what says this is an aside rather than a paragraph */
  .gist { padding-left: 0.6rem; }
  .leaf { grid-template-columns: 2.6rem 2.2rem minmax(0, 1fr) auto; gap: 0.5rem; }
  /* four stacked lines is a lot of corner on a phone; tightened up they are
     still four lines somebody can read at a glance */
  .credits { gap: 0.28rem; }
  .stats { grid-template-columns: 1fr 1fr; }
  .stats span { font-size: 0.44rem; }
  .turn .lab { display: none; }
  .spine { padding: 0 0.35rem; gap: 0.3rem; }
  .spine .face { display: none; }
  /* A phone reads a pilot page as one column of eleven cels, one under the
     next, and at that width the gap between two of them is the only thing
     saying they are two. It is the tab's own height again, plus the room a
     thumb needs to not think they are one panel. */
  .page main.who { row-gap: 2.3rem; }
  .ch.who > .cel { padding: 1.7rem 0.9rem 1.15rem; }
  .tiles { grid-template-columns: minmax(0, 1fr); }
  .traits { gap: 0.3rem; }
  /* nine marks against a bar and a number is four columns too many here: the
     bar goes and the number does the saying */
  .prints .mk { grid-template-columns: 0.9rem minmax(0, 1fr) 2rem; gap: 0.5rem; }
  .prints .bar { display: none; }
  .board { font-size: 0.56rem; }
  .board th:nth-child(4), .board td:nth-child(4),
  .board th:nth-child(5), .board td:nth-child(5) { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  h1, .shout, .roster, .art *, .dots, .plate-img, .loud, .loud::before { animation: none; }
  .js .cel { opacity: 1; transform: none; transition: none; }
  /* the plate still opens, it just does not fly there */
  .lit { transition: none; }
  /* the sound plays, the meter holds still - it was only ever saying so */
  .song.on .eq i { animation: none; height: 60%; }
  /* the book still opens, the arrow just points the other way at once */
  .book > summary::before { transition: none; }
  .leaf .thumb img { transition: none; }
}
