/* The landing page: one tall cross-section, read top to bottom.
   sky -> deck -> waterline -> below deck -> the hold -> seabed.
   Every sprite is sized in --px units (4px, or 3px on a phone) so the page has one pixel grid. */
html,
body {
  overflow-x: clip;
}
body {
  background: var(--c8);
} /* the last thing on the page is the footer, so a fractional page height never shows a cream hairline under it */
main {
  display: block;
}
.wrapw {
  width: min(1328px, calc(100% - 2 * var(--gut)));
  margin-inline: auto;
}
.place {
  color: var(--sea);
}
h2 .place {
  margin-right: 0.35em;
}
.nb {
  white-space: nowrap;
}
h2,
h3,
h4 {
  text-wrap: balance;
}
.intro {
  max-width: 62ch;
  margin: 0 0 36px;
  font-size: var(--t-lg);
}
section[id] {
  scroll-margin-top: var(--bar-h);
}

.links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin-top: 22px;
  font-weight: 700;
}

/* ------------------------------------------------------------ sky */
.sky {
  position: relative;
  overflow: hidden;
  background: linear-gradient(var(--sky-high) 0 44px, transparent 44px), var(--sky);
  padding: 110px var(--gut) 0;
}
.sky::before {
  /* a dithered step between the two sky blues, 4px cells */
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 44px;
  height: 16px;
  background: repeating-conic-gradient(var(--sky) 0 25%, var(--sky-high) 0 50%) 0 0 / 8px 8px;
}
.sky-art {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.cloud {
  position: absolute;
  animation: drift 140s linear infinite;
}
.cloud.a {
  top: 150px;
  left: 52%;
}
.cloud.b {
  top: 560px;
  left: 49%;
  animation-duration: 190s;
  animation-delay: -60s;
}
.cloud.c {
  top: 46px;
  left: 40%;
  animation-duration: 170s;
  animation-delay: -90s;
}
@keyframes drift {
  from {
    transform: translateX(0);
  }
  50% {
    transform: translateX(120px);
  }
  to {
    transform: translateX(0);
  }
}
.gull {
  position: absolute;
  width: calc(19 * var(--px));
  height: calc(9 * var(--px));
  background: url("../art/gull.svg") 0 0 / calc(38 * var(--px)) calc(9 * var(--px));
  image-rendering: pixelated;
  animation:
    flap 1s steps(1) infinite,
    glide 26s ease-in-out infinite alternate;
}
.g1 {
  left: 56%;
  top: 230px;
}
.g2 {
  left: 63%;
  top: 330px;
  animation-duration: 1.2s, 34s;
  animation-delay:
    -0.4s,
    -12s;
}
.g3 {
  left: 53%;
  top: 470px;
  animation-duration: 0.9s, 30s;
  animation-delay:
    -0.2s,
    -20s;
}
@keyframes flap {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: calc(-19 * var(--px)) 0;
  }
}
@keyframes glide {
  from {
    transform: translate(0, 0);
  }
  to {
    transform: translate(110px, -26px);
  }
}

.hero {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 56px;
  width: min(1328px, 100%);
  margin: 0 auto;
}
.hero-text {
  padding-bottom: 130px;
}
.hero h1 {
  margin: 0 0 28px;
  max-width: 15ch;
}
.lede {
  font-size: var(--t-xl);
  line-height: 1.5;
  max-width: 34ch;
  margin: 0 0 36px;
}
/* the sentence that says what ship does: a step up from body text, so it reads second after the lede (on a phone the lede is
   already this size, so it stays at body size there) */
.hero-text > p:not(.lede) {
  font-size: var(--t-lg);
  max-width: 44ch;
}
.install-quick p {
  margin: 12px 0 0;
  font-size: var(--t-md);
  color: var(--muted-light);
}

.rig {
  position: relative;
  min-height: 640px;
}
.nest {
  position: absolute;
  right: calc(5 * var(--px));
  top: -60px;
  width: calc(44 * var(--px));
  height: calc(48 * var(--px));
}
/* the lookout's spyglass sweeps from the horizon down to the agent list and back */
.a-spyglass {
  position: absolute;
  left: 0;
  top: calc(12 * var(--px));
  --w: 12;
  --h: 16;
  --n: 14;
  --dt: 0.5s;
  --lag: -2s;
  background-image: url("../art/spyglass-loop.svg");
}
.mast,
.ratlines {
  position: absolute;
  image-rendering: pixelated;
}
.mast {
  right: calc(22 * var(--px));
  width: calc(6 * var(--px));
  top: calc(-60px + 48 * var(--px));
  bottom: 0;
  background: url("../art/mast.svg") repeat-y left bottom / calc(6 * var(--px)) calc(16 * var(--px));
}
.ratlines {
  right: calc(14 * var(--px));
  width: calc(8 * var(--px));
  top: calc(-60px + 52 * var(--px));
  bottom: 0;
  background: url("../art/ratlines.svg") repeat-y left bottom / calc(8 * var(--px)) calc(8 * var(--px));
}
.mast-foot {
  display: none;
}
.board {
  position: absolute;
  left: 0;
  top: 260px;
  width: min(300px, calc(100% - 32 * var(--px))); /* stays clear of the mast when the rig is narrow */
  margin: 0;
}
.board .term {
  padding: 6px 0 10px;
}
.board .head {
  padding: 0 12px;
  color: var(--muted-dark);
  font-weight: 700;
  overflow: hidden;
  white-space: nowrap;
}
.board ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.board li {
  padding: 3px 12px;
}
.board li span.tab {
  display: block;
  color: var(--muted-dark);
}
.board figcaption {
  font-size: var(--t-sm);
  color: var(--muted-light);
  margin-top: 10px;
  max-width: min(30ch, 100%);
}

/* ---- the agents board hangs from a yardarm on the mast, two chains down to the bezel (900px and up; below
   that the board sits in the flow under the nest). To drop it, remove this block. */
@media (min-width: 900px) {
  .rig::before {
    content: "";
    position: absolute;
    left: calc(-4 * var(--px));
    right: calc(28 * var(--px));
    top: 180px;
    height: calc(4 * var(--px));
    background:
      linear-gradient(var(--ink) 0 0) left / var(--px) 100% no-repeat,
      linear-gradient(
        var(--ink) 0 var(--px),
        var(--c11) var(--px) calc(2 * var(--px)),
        var(--c3) calc(2 * var(--px)) calc(3 * var(--px)),
        var(--ink) calc(3 * var(--px)) 100%
      );
    clip-path: polygon(
      0 var(--px),
      var(--px) var(--px),
      var(--px) 0,
      100% 0,
      100% 100%,
      var(--px) 100%,
      var(--px) calc(100% - var(--px)),
      0 calc(100% - var(--px))
    );
  }
  .board::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -64px;
    height: 64px;
    background:
      url("../art/chain.svg") repeat-y left calc(2 * var(--px)) top 0 / calc(4 * var(--px)) calc(8 * var(--px)),
      url("../art/chain.svg") repeat-y right calc(2 * var(--px)) top 0 / calc(4 * var(--px)) calc(8 * var(--px));
    image-rendering: pixelated;
  }
}

/* ambient face patches: a neutral crewmate blinks, and glances up when its room scrolls into view */
.eyes {
  position: absolute;
  display: block;
  pointer-events: none;
  opacity: 0;
  left: calc(var(--x) * var(--px));
  top: calc(var(--y) * var(--px));
  width: calc(10 * var(--px));
  height: calc(6 * var(--px));
  background: url("../art/face-blink.svg") 0 0 / 100% 100%;
  image-rendering: pixelated;
  animation: blink 7s steps(1) infinite;
}
.looking .eyes {
  background-image: url("../art/face-glance.svg");
  opacity: 1;
  animation: none;
}
@keyframes blink {
  0%,
  93% {
    opacity: 0;
  }
  94%,
  96% {
    opacity: 1;
  }
  97%,
  100% {
    opacity: 0;
  }
}
.nest .eyes {
  z-index: 1;
}
/* ---- the neutral crewmates follow the pointer: site.js sets --lx and --ly (0, 1, 2) and adds .look when the
   pointer is off to one side, and the face sheet slides to that cell. To drop it, remove this block and
   eyesFollow() in site.js. */
.eyes.look {
  background-image: url("../art/face-look.svg");
  background-size: 300% 300%;
  background-position: calc(var(--lx, 1) * 50%) calc(var(--ly, 1) * 50%);
  opacity: 1;
  animation: none;
}

/* -------------------------------------------------------- deck */
.deck {
  position: relative;
  background: var(--haze);
  padding: 80px 0 0;
  overflow: hidden;
}
.deck::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 16px;
  background: repeating-conic-gradient(var(--haze) 0 25%, var(--sky) 0 50%) 0 0 / 8px 8px;
}
.deck-in {
  width: min(1328px, calc(100% - 2 * var(--gut)));
  margin-inline: auto;
  padding-right: calc(30 * var(--px));
}
.deck > .mast,
.deck > .ratlines {
  right: calc(max(var(--gut), 50% - 664px) + 22 * var(--px));
  top: 0;
  bottom: calc(32 * var(--px));
}
.deck > .ratlines {
  right: calc(max(var(--gut), 50% - 664px) + 14 * var(--px));
  background-position: left top;
}
.deck > .mast {
  background-position: left top;
}
.deck > .mast::after {
  content: "";
  position: absolute;
  left: calc(-4 * var(--px));
  bottom: 0;
  width: calc(14 * var(--px));
  height: calc(8 * var(--px));
  background: url("../art/mast-base.svg") 0 0 / 100% 100%;
  image-rendering: pixelated;
}
.deck h2 {
  margin: 0 0 20px;
}
/* what works today: a ruled ledger, the ten names down the left so they read at a glance and the words beside them */
.works {
  list-style: none;
  margin: 0;
  padding: 0;
  --rule: color-mix(in srgb, var(--ink) 38%, transparent);
  --dash: linear-gradient(to right, var(--rule) 4px, transparent 4px) 0 0 / 8px 2px repeat-x;
}
/* the ten rows come in three groups, each headed by a crewmate standing on the group's first rule (solid, so it is the
   ground), then the plain label. A row is the same ledger row as before, so the climbing navigator and the selected
   row work as they did */
.works-groups {
  display: grid;
  gap: 40px;
}
.wg-head {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  margin-bottom: calc(-3 * var(--px)); /* the frame has three empty rows under the feet */
}
.wg-head h3 {
  margin: 0 0 10px;
  color: var(--sea);
}
.wg-crew {
  position: relative;
  flex: none;
  display: block;
  line-height: 0;
  margin-left: calc(-9 * var(--px)); /* the crewmate's left arm lines up with the rows' bullets */
}
.wgroup .works li:first-child {
  --dash: linear-gradient(var(--rule) 0 0) 0 0 / 100% 2px no-repeat;
}
/* a crewmate that does one thing when you press it, and is otherwise still: frame 0 of its sheet is the resting pose, and
   the press plays the sheet once (site.js, crew.js, adds .react to the host). At night the outline is bright blue, as the diver's */
.act1 {
  display: block;
  flex: none;
  width: calc(var(--w) * var(--px));
  height: calc(var(--h) * var(--px));
  background: no-repeat 0 0 / calc(var(--w) * var(--n) * var(--px)) calc(var(--h) * var(--px));
  image-rendering: pixelated;
}
.react > .act1 {
  animation: act calc(var(--n) * var(--bdt)) steps(var(--n)) 1;
}
.react > .eyes {
  opacity: 0 !important;
  animation: none;
}
.b-captain {
  --w: 36;
  --h: 28;
  --n: 10;
  --bdt: 0.1s;
  background-image: url("../art/beat-captain.svg");
}
.b-lookout {
  --w: 36;
  --h: 28;
  --n: 12;
  --bdt: 0.085s;
  background-image: url("../art/beat-lookout.svg");
}
.b-deckhand {
  --w: 36;
  --h: 28;
  --n: 10;
  --bdt: 0.1s;
  background-image: url("../art/beat-deckhand.svg");
}
[data-mode="dark"] .b-captain {
  background-image: url("../art/beat-captain-night.svg");
}
[data-mode="dark"] .b-lookout {
  background-image: url("../art/beat-lookout-night.svg");
}
[data-mode="dark"] .b-deckhand {
  background-image: url("../art/beat-deckhand-night.svg");
}
/* the props over the deck crew (tools/beats.py): 44 by 40, with the crew's own 36 by 28 frame at (4, 8) so there is room above
   the hat. Frame 0 is empty, so at rest nothing shows; a press plays the sheet once (crew.js adds .react) and lights the plate
   as alt-a does. They are drawn beside and above the crewmate, never over its face, so the glyph, the colour and the word
   keep saying the state. */
.member > .fx {
  position: absolute;
  left: calc(-8 * var(--px));
  top: calc(-8 * var(--px));
  z-index: 1;
  pointer-events: none;
  --w: 44;
  --h: 40;
  --n: 10;
  --bdt: 0.1s;
}
.fx-lookout {
  background-image: url("../art/fx-lookout.svg");
}
.fx-navigator {
  background-image: url("../art/fx-navigator.svg");
}
.fx-deckhand {
  background-image: url("../art/fx-deckhand.svg");
}
.fx-cook {
  background-image: url("../art/fx-cook.svg");
}
.fx-captain {
  background-image: url("../art/fx-captain.svg");
}
[data-mode="dark"] .fx-lookout {
  background-image: url("../art/fx-lookout-night.svg");
}
[data-mode="dark"] .fx-navigator {
  background-image: url("../art/fx-navigator-night.svg");
}
[data-mode="dark"] .fx-deckhand {
  background-image: url("../art/fx-deckhand-night.svg");
}
[data-mode="dark"] .fx-cook {
  background-image: url("../art/fx-cook-night.svg");
}
[data-mode="dark"] .fx-captain {
  background-image: url("../art/fx-captain-night.svg");
}
/* the nest lookout, pressed: one quick sweep of the glass, then back to its slow one */
@keyframes act-once {
  to {
    background-position-x: calc(var(--w) * var(--n) * var(--px) * -1);
  }
}
.nest.react > .a-spyglass {
  animation: act-once calc(var(--n) * 0.07s) steps(var(--n)) 1;
}
/* the diver, pressed: a string of bubbles from the helmet (the sprite's own loop carries on) */
.diver {
  pointer-events: auto;
}
.diver > .pop {
  position: absolute;
  left: calc(var(--x) * var(--px));
  top: calc(4 * var(--px));
  width: calc(3 * var(--px));
  height: calc(3 * var(--px));
  background: url("../art/bubble.svg") 0 0 / 100% 100%;
  image-rendering: pixelated;
  opacity: 0;
  pointer-events: none;
}
.diver.react > .pop {
  animation: blow 1.4s steps(8) var(--d) 1 forwards;
}
@keyframes blow {
  0% {
    opacity: 1;
    transform: translate(0, 0);
  }
  85% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(calc(var(--dx) * var(--px)), calc(var(--rise) * var(--px) * -1));
  }
}
/* what can be pressed says so with the pointer, and the deck crew take a small hop when you come near (a pixel, not a loop) */
@media (prefers-reduced-motion: no-preference) {
  [data-react],
  .member {
    cursor: pointer;
  }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .member:hover > .act,
  .member:hover > .fx {
    transform: translateY(calc(-1 * var(--px)));
  }
}
.works::after {
  content: "";
  display: block;
  height: 2px;
  background: var(--dash);
}
.works li {
  display: grid;
  grid-template-columns: 344px minmax(0, 1fr); /* from 1280px the column is wide enough for the longest title, its bullet and its marker on one line (below, the long ones wrap) */
  gap: 0 40px;
  align-items: baseline;
  margin: 0;
  padding: 18px 0 16px;
  background: var(--dash);
}
.works h4 {
  margin: 0;
  color: var(--ink);
  display: flex;
  gap: 12px;
  align-items: baseline;
}
.works h4::before {
  content: "";
  flex: none;
  width: 12px;
  height: 12px;
  background: var(--sea);
  transform: translateY(1px);
}
.works p {
  margin: 0;
  max-width: 62ch;
  font-size: var(--t-base);
}
@media (min-width: 1280px) {
  .works li {
    grid-template-columns: 440px minmax(0, 1fr);
  }
}
.notyet {
  margin-top: 24px;
  border: 3px dashed var(--ink);
  padding: 22px 28px 8px;
}
.notyet h3 {
  margin: 0 0 14px;
}
.notyet ul {
  columns: 2;
  column-gap: 56px;
  margin: 0;
  font-size: var(--t-base);
}
.notyet li {
  break-inside: avoid;
  margin: 0 0 14px;
}
.crew-note {
  margin: 44px 0 0;
  color: var(--muted-light);
  font-size: var(--t-md);
  max-width: 60ch;
}

.stagewrap {
  position: relative;
  margin-top: 20px;
}
.rail-far {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: url("../art/rail.svg");
}
.stage {
  position: relative;
  width: min(1328px, calc(100% - 2 * var(--gut)));
  margin-inline: auto;
  padding-right: calc(30 * var(--px));
}
.crew {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
}
.crew > li {
  position: relative;
  margin-bottom: calc(-3 * var(--px));
}
.prop {
  display: flex;
  align-items: flex-end;
  gap: calc(2 * var(--px));
  margin-bottom: 0 !important;
  padding-bottom: 0;
}
.prop img {
  margin-bottom: 0;
}
.member {
  width: calc(28 * var(--px));
}
.member > .act {
  margin-inline: calc((28 - var(--w)) / 2 * var(--px));
} /* the frame is wider than the crewmate: centre it */
.plate {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  z-index: 2;
  background: var(--ink);
  color: var(--c15);
  font: var(--code-md)/22px var(--code);
  padding: 2px 12px 3px;
  text-align: center;
  white-space: nowrap;
  box-shadow: 0 0 0 2px var(--ink);
}
.plate .g {
  margin-right: 4px;
}
.plate .role {
  display: block;
  color: var(--muted-dark);
  font-size: var(--code-sm);
  line-height: 20px;
}
.planks,
.hull {
  background-image: url("../art/planks.svg");
}
.hull {
  background-image: url("../art/hull.svg");
}

/* ---------------------------------------------------- waterline */
.waterline {
  position: relative;
  background: var(--sea);
  color: var(--c15);
  padding: 0 0 150px;
  --ring: var(--c14);
}
.waves {
  position: relative;
  z-index: 3;
  background-color: var(--ink);
  background-image: url("../art/wave.svg");
  animation: swell 9s linear infinite;
}
.waves.back {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background-color: transparent;
  background-image: url("../art/wave-back.svg");
  animation: swell 16s linear infinite reverse;
  margin-top: calc(-0 * var(--px));
}
@keyframes swell {
  to {
    background-position-x: calc(16 * var(--px));
  }
}
.shade {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(5 * var(--px));
  z-index: 0;
}
.shade i {
  display: block;
  height: 16px;
}
.shade .d1 {
  background: repeating-conic-gradient(var(--c4) 0 25%, var(--c12) 0 50%) 0 0 / 8px 8px;
}
.shade .s1 {
  height: 8px;
  background: var(--c4);
}
.shade .d2 {
  background: repeating-conic-gradient(var(--sea) 0 25%, var(--c4) 0 50%) 0 0 / 8px 8px;
}
.slop {
  position: absolute;
  inset: 20px 0 0;
  font: 22px/44px var(--pixel);
  color: #2b5cb6;
  white-space: nowrap;
  pointer-events: none;
  overflow: hidden;
}
.slop div {
  animation: slosh 60s ease-in-out infinite alternate;
}
/* the first row under the waves is the only one with no copy over it, so it is the one that gets read: bright blue,
   about 3:1 on the sea. The first line (under the swell's dither) and the rest (behind the copy) stay faint. It is
   decoration and aria-hidden, so the page reads the same without it */
.slop div:nth-child(2) {
  color: var(--c12);
}
.slop div:nth-child(even) {
  animation-direction: alternate-reverse;
  margin-left: -300px;
}
@keyframes slosh {
  to {
    transform: translateX(-180px);
  }
}
.flotsam {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 4;
  pointer-events: none;
}
.flotsam img {
  position: absolute;
  top: calc(14px - var(--h) * var(--px) * 0.7);
  animation: bob 5s steps(2) infinite;
}
.f2 {
  left: 41%;
  animation-delay: -1.6s !important;
}
.f3 {
  left: 68%;
  animation-delay: -3s !important;
}
.f4 {
  left: 86%;
  animation-delay: -0.8s !important;
}
/* more things adrift (tools/waterline.py): a crate for a pull request nobody read, and the title of one on a plank */
.f5 {
  left: 30%;
  animation-delay: -2.4s !important;
}
.f6 {
  left: 3%;
  animation-delay: -4.1s !important;
}
@media (min-width: 1250px) {
  .f3 {
    left: 74%;
  } /* the dinghy takes the gap at 47%, and its net reaches the buoy's old place at 1250px */
}
/* the cook's dinghy: a one-shot beat like the rooms' (a press plays it once, and so does scrolling it into view),
   with the page's own bob moving the boat. At the surface from 1250px, where the flotsam row has a gap for it. Narrower,
   the same boat floats under the words, between them and the recording, because the row is full */
.dinghy {
  position: absolute;
  left: 47%;
  top: calc(14px - 22 * var(--px));
  width: calc(78 * var(--px));
  height: calc(38 * var(--px));
  line-height: 0;
  pointer-events: auto;
  animation: bob 5s steps(2) infinite;
  animation-delay: -2.2s;
}
.b-dinghy {
  --w: 78;
  --h: 38;
  --n: 13;
  --bdt: 0.11s;
  background-image: url("../art/beat-dinghy.svg");
}
.dinghy-flow {
  display: none;
}
@keyframes bob {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(calc(1 * var(--px)));
  }
}
.deepwater {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.3;
}
.fish {
  position: absolute;
  background: url("../art/fish.svg") 0 0 / calc(24 * var(--px)) calc(6 * var(--px));
  image-rendering: pixelated;
  animation:
    tail 0.7s steps(1) infinite,
    swim 60s linear infinite;
}
/* the three here swim through the empty water under the copy (the section's last 150px, above the seafloor), so none
   crosses a caption */
.fish.s1 {
  bottom: 118px;
  left: 0;
  animation-delay:
    0s,
    -10s;
}
.fish.s2 {
  bottom: 92px;
  left: 0;
  animation-delay:
    -0.3s,
    -34s;
  animation-duration: 0.8s, 80s;
}
.fish.s3 {
  bottom: 104px;
  left: 0;
  animation-delay:
    -0.5s,
    -50s;
  animation-duration: 0.6s, 70s;
}
@keyframes tail {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: calc(-12 * var(--px)) 0;
  }
}
@keyframes swim {
  from {
    transform: translateX(calc(100vw + 60px));
  }
  to {
    transform: translateX(-200px);
  }
}
.bubble {
  position: absolute;
  bottom: -20px;
  animation: rise 18s linear infinite;
}
.b1 {
  left: 22%;
}
.b2 {
  left: 57%;
  animation-delay: -7s;
  animation-duration: 24s;
}
.b3 {
  left: 91%;
  animation-delay: -13s;
  animation-duration: 20s;
}
@keyframes rise {
  to {
    transform: translateY(-900px);
  }
}

.water-in {
  position: relative;
  z-index: 1;
  width: min(1328px, calc(100% - 2 * var(--gut)));
  margin: 0 auto;
  padding-top: 99px;
  display: grid;
  grid-template-columns: minmax(0, 1fr); /* the words, then the session at the full width, so its text is readable */
  gap: 48px;
  align-items: start;
}
.waterline .place {
  color: var(--c14);
}
.water-in h2 {
  margin: 0 0 22px;
}
.plain {
  font: 11px/22px var(--pixel);
  display: inline-block;
  vertical-align: 4px;
  margin-left: 8px;
  padding: 0 8px;
  background: var(--c14);
  color: var(--ink);
}
.water-copy {
  background: var(--sea);
  box-shadow: 0 0 0 22px var(--sea);
}
.water-copy p {
  max-width: 58ch;
  margin: 0 0 18px;
  font-size: var(--t-lg);
}
.recording {
  margin: 0;
}
.recording video {
  display: block;
  width: 100%;
  height: auto;
  background: var(--ink);
}
.recording figcaption {
  margin-top: 10px;
  color: var(--c7);
  font-size: var(--t-md);
}
.seafloor-dither {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 0;
}
.seafloor-dither i {
  display: block;
}
.seafloor-dither .d1 {
  height: 16px;
  background: repeating-conic-gradient(var(--deep) 0 25%, var(--sea) 0 50%) 0 0 / 8px 8px;
}
.seafloor-dither .s1 {
  height: 64px;
  background: var(--deep);
}

/* ---------------------------------------------------- below deck */
.below {
  position: relative;
  color: var(--c15);
  --ring: var(--c14);
  background:
    url("../art/wall.svg") 0 0 / calc(32 * var(--px)) calc(22 * var(--px)),
    var(--wall);
}
.bulkhead {
  background-image: url("../art/bulkhead.svg");
}
.below .term,
.hold .term,
.seabed .term {
  border-color: var(--c8);
}
.below .place,
.hold .place {
  color: var(--c14);
}
.below-in > :not(.beam) {
  width: min(1328px, calc(100% - 2 * var(--gut)));
  margin-inline: auto;
}
.below-in {
  padding-top: 72px;
}
.below h2 {
  margin: 0 0 18px;
}
.below-head .intro {
  margin-bottom: 0;
}
.below .intro {
  color: var(--c7);
}
.floor {
  position: relative; /* the background crew stand on the floor's bottom edge (the beam) or hang on its wall */
  display: grid; /* the columns are set below: two from 1100px, then the stacked layouts */
  gap: 48px;
  align-items: start;
  padding: 48px 0 56px;
}
.room {
  position: relative;
  width: max-content;
  line-height: 0;
}
/* each room is a sheet of one beat: the room at rest in frame 0, then anticipation, action and reaction, back to rest
   (tools/crewrooms.py). It plays once when the room scrolls into view and again when you press it (crew.js adds .react) */
.r-galley {
  --w: 84;
  --h: 56;
  --n: 18;
  --bdt: 0.1s;
  background-image: url("../art/room-galley.svg");
}
.r-chart {
  --w: 84;
  --h: 56;
  --n: 17;
  --bdt: 0.11s;
  background-image: url("../art/room-chart.svg");
}
.r-telegraph {
  --w: 84;
  --h: 56;
  --n: 16;
  --bdt: 0.1s;
  background-image: url("../art/room-telegraph.svg");
}
.r-handoff {
  --w: 84;
  --h: 56;
  --n: 16;
  --bdt: 0.11s;
  background-image: url("../art/room-handoff.svg");
}
.r-hold {
  --w: 84;
  --h: 56;
  --n: 20;
  --bdt: 0.1s;
  background-image: url("../art/room-hold.svg");
}
/* the hold's card scenes (tools/holdscenes.py): the same stage and the same one-shot beat as the rooms above, one at the head
   of each card. The lantern is a separate loop, so it swings whether or not the beat is playing */
.r-keys,
.r-config,
.r-agents,
.r-mouse,
.r-saved {
  --w: 84;
  --h: 56;
}
.r-keys {
  --n: 12;
  --bdt: 0.1s;
  background-image: url("../art/room-keys.svg");
}
.r-config {
  --n: 14;
  --bdt: 0.1s;
  background-image: url("../art/room-config.svg");
}
.r-agents {
  --n: 12;
  --bdt: 0.11s;
  background-image: url("../art/room-agents.svg");
}
.r-mouse {
  --n: 16;
  --bdt: 0.1s;
  background-image: url("../art/room-mouse.svg");
}
.r-saved {
  --n: 14;
  --bdt: 0.1s;
  background-image: url("../art/room-saved-tabs.svg");
}
.a-lamp {
  --w: 13;
  --h: 15;
  --n: 10;
  --dt: 0.5s;
  position: absolute;
  left: calc(6 * var(--px));
  top: calc(1 * var(--px));
  background-image: url("../art/lantern-sway.svg");
}
.a-lamp.r {
  left: calc(66 * var(--px));
}
.floor-text h3 {
  margin: 0 0 12px;
  color: var(--c15);
}
.floor-text p {
  margin: 0 0 14px;
  font-size: var(--t-base);
  color: var(--c7);
  max-width: 52ch;
}
.floor-fig {
  margin: 0;
  container-type: inline-size; /* the sketch's panes stack by the width of its figure: see @container below */
}
.floor-fig figcaption {
  font-size: var(--t-sm);
  color: var(--muted-dark);
  margin-top: 10px;
  max-width: 60ch;
}
.floor-fig .cmd pre {
  font-size: var(--code-sm);
}
.beam {
  background-image: url("../art/beam.svg");
}
.beam.last {
  margin-top: 0;
}

/* the server, drawn in the terminal's own boxes */
.dg {
  padding: 18px;
  font: var(--code-md)/1.4 var(--code);
}
.dg-wins {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
.dg-win {
  position: relative;
  border: 2px solid var(--c14);
  padding: 6px 10px 8px;
  color: var(--c15);
  display: flex;
  flex-direction: column;
}
.dg-win b {
  font: 11px/22px var(--pixel);
  color: var(--c14);
  font-weight: 400;
}
.dg-win::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  height: 22px;
  border-left: 2px solid var(--c14);
}
.dg-win.gone {
  border: 2px dashed var(--c8);
  color: var(--muted-dark);
}
.dg-win.gone b {
  color: var(--muted-dark);
}
.dg-win.gone::after {
  border-left: 2px dashed var(--c8);
}
.dg-server {
  margin-top: 22px;
  border: 2px solid var(--c11);
  padding: 10px 14px 14px;
}
.dg-label {
  font: 11px/22px var(--pixel);
  color: var(--c11);
  display: block;
  margin-bottom: 6px;
  font-weight: 400;
}
.dg-panes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.dg-pane {
  border: 2px solid var(--c8);
  padding: 5px 10px;
  color: var(--c15);
}

/* a hand-drawn sketch of ship's layout */
.ui {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  font: var(--code-sm)/1.4 var(--code);
}
.ui pre {
  white-space: pre;
  overflow: hidden;
}
.ui-side {
  border-right: 2px solid var(--c8);
  padding: 8px 6px;
  color: var(--c7);
}
.ui-main {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  padding: 8px;
}
.ui-pane {
  border: 2px solid var(--c8);
  padding: 6px 8px;
  color: var(--c7);
  min-width: 0;
}
.ui-pane:first-child {
  grid-row: span 2;
}
.ui-pane.sel {
  border-color: var(--c6);
}
/* beside the text on a laptop the sketch gets too narrow for two columns of panes, so they stack rather than cut their words */
@container (max-width: 449px) {
  .ui-main {
    grid-template-columns: minmax(0, 1fr);
  }
  .ui-pane:first-child {
    grid-row: auto;
  }
}

/* ---- recording controls: site.js adds .reel to the bezel's chin, a pause button and a groove you can scrub, and
   takes the native controls away (they stay with reduced motion, or without the script). To drop it, remove
   this block, reelControls() in site.js and the .reel-btn selectors in site.css. */
.screen {
  position: relative;
}
.screen.has-reel {
  border-bottom-width: calc(10 * var(--px));
}
.reel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(-9 * var(--px));
  height: calc(9 * var(--px));
  display: flex;
  align-items: center;
  gap: calc(3 * var(--px));
}
.reel-btn {
  position: relative;
  flex: none;
  line-height: 18px;
  padding: 1px 10px 2px;
}
.reel-btn::after {
  content: "";
  position: absolute;
  inset: -4px;
} /* a 24px target around the 18px keycap */
.reel-btn:focus-visible {
  outline-color: var(--ink);
  outline-offset: 2px;
}
.reel-track {
  position: relative;
  flex: 1;
  height: calc(4 * var(--px));
  background: var(--ink);
  cursor: pointer;
  touch-action: pan-y; /* a sideways drag scrubs; a vertical swipe still scrolls the page */
}
.reel-track::before {
  content: "";
  position: absolute;
  inset: calc(-2 * var(--px)) 0;
} /* a taller target than the groove */
.reel-track:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}
@media (pointer: coarse) {
  .reel-btn::after {
    inset: -12px -4px;
  } /* 44px */
  .reel-track::before {
    inset: -16px 0;
  } /* 44px or more */
}
.reel-track i {
  position: absolute;
  left: var(--px);
  top: var(--px);
  bottom: var(--px);
  width: calc((100% - 2 * var(--px)) * var(--p, 0));
  background: var(--c6);
}

/* ---- ship's keys on this page (site.js, shipKeys): alt-a calls the next crewmate who needs you, and the
   plate turns the selection cyan while the sprite takes one jolt; alt-q detaches with a toast. To drop it,
   remove this block and shipKeys() in site.js. */
.member.called .plate {
  background: var(--c6);
  color: var(--ink);
  box-shadow: 0 0 0 2px var(--c6);
}
.member.called .plate .g,
.member.called .plate .role {
  color: var(--ink);
}
.member.called > .act {
  animation:
    act calc(var(--n) * var(--dt)) steps(var(--n)) var(--lag, 0s) infinite,
    jolt 0.3s steps(1) 3;
}
@keyframes jolt {
  50% {
    transform: translateY(calc(-1 * var(--px)));
  }
}
.toast {
  position: fixed;
  left: 50%;
  bottom: 20px;
  z-index: 80;
  width: max-content;
  max-width: min(520px, calc(100% - 2 * var(--gut)));
  padding: 12px 16px;
  color: var(--c15);
  transform: translateX(-50%);
  animation: toast-in 0.24s steps(3);
}
.toast[hidden] {
  display: none;
}
@keyframes toast-in {
  from {
    transform: translate(-50%, calc(3 * var(--px)));
  }
}

/* ---- floor layout, 1100px and up: the room over its words on the left,
   the screen centred on the right like a monitor on the wall. The words get a 54ch line, and a recording is
   wider than the waterline one at every width here. Narrower screens keep the stacked layout further down. */
@media (min-width: 1100px) {
  .floor {
    grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); /* the screen gets two thirds: a recording's text is legible */
    grid-template-areas:
      "room fig"
      "text fig";
    column-gap: 64px;
    row-gap: 28px;
  }
  .floor > .room {
    grid-area: room;
  }
  .floor > .floor-text {
    grid-area: text;
  }
  .floor > .floor-fig,
  .floor > .recording {
    grid-area: fig;
    align-self: center;
  }
  .floor-text p {
    max-width: 54ch;
  }
}

/* ------------------------------------------------------- the hold */
.hold {
  position: relative;
  background: var(--hold);
  color: var(--c15);
  padding: 0 0 calc(18 * var(--px) + 130px);
  overflow: hidden;
  --ring: var(--c14);
}
.hold-in {
  width: min(1328px, calc(100% - 2 * var(--gut)));
  margin: 0 auto;
  padding-top: 80px;
  position: relative;
  z-index: 1;
}
.hold-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 48px;
  align-items: end;
}
.hold h2 {
  margin: 0 0 18px;
}
.hold .intro {
  color: var(--c7);
  margin-bottom: 0;
}
.cargo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: 56px 64px;
  margin-top: 64px;
  align-items: start;
}
@media (min-width: 1280px) {
  .cargo {
    grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
  }
} /* keys is the long column, so it gets the room: fewer wrapped rows */
.crate-col h3 {
  margin: 0 0 12px;
  color: var(--c11);
}
/* a scene at the head of a card. The keys card (its column is the widest) sets its words beside the scene from 1280px */
.card-room {
  margin: 0 0 24px;
}
@media (min-width: 1280px) {
  .card-head {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    gap: 0 36px;
    align-items: end;
    margin-bottom: 24px;
  }
  .card-head .card-room {
    margin: 0;
  }
  .card-head p:last-child {
    margin-bottom: 0;
  }
}
.crate-col p {
  color: var(--c7);
  font-size: var(--t-base);
  max-width: 58ch;
  margin: 0 0 16px;
}
.crate-col .cmd {
  margin: 0 0 16px;
}
.crate-col .note {
  color: var(--c15);
  border-top: 2px dashed var(--c8);
  padding-top: 14px;
}
.needs {
  margin: 0 0 20px;
  color: var(--c7);
  font-size: var(--t-base);
}
.needs li {
  margin-bottom: 8px;
}
.keys {
  border-collapse: collapse;
  width: 100%;
  margin: 0 0 18px;
}
.keys th,
.keys td {
  text-align: left;
  vertical-align: baseline;
  padding: 6px 14px 6px 0;
  border-bottom: 1px solid #3a3650;
  font-weight: 400;
}
.keys th {
  white-space: nowrap;
  width: 1%;
}
.keys td {
  color: var(--c7);
  font-size: var(--t-md);
  line-height: 1.4;
}
.keys td kbd {
  font-size: var(--code-sm);
  padding: 1px 5px 2px;
  box-shadow: 0 2px 0 var(--c8);
  background: #2b2840;
  margin-bottom: 0;
}
.keys th kbd {
  background: #2b2840;
}
#config {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.6fr);
  column-gap: 64px;
  align-items: start;
}
#config > * {
  grid-column: 1;
}
#config > .term {
  grid-column: 2;
  grid-row: 1 / span 4;
}
/* beside each other, the config card is two rows: the scene with the heading and its first paragraph beside it, then the rest of
   the words beside the code they talk about. The flexible rows take up what the scene and the code are taller than their words */
@media (min-width: 1200px) {
  #config {
    grid-template-rows: 1fr auto auto 1fr;
  }
  #config > .card-room {
    grid-column: 1;
    grid-row: 1 / span 2;
    margin: 0;
  }
  #config > h3 {
    grid-column: 2;
    grid-row: 1;
    align-self: end;
  }
  #config > h3 + p {
    grid-column: 2;
    grid-row: 2;
  }
  #config > .term {
    grid-row: 3 / span 2;
    margin-top: 16px;
  }
  #config > .term + p {
    grid-row: 3;
    margin-top: 16px;
  }
  #config > .more {
    grid-row: 4;
  }
}
/* the hold's recordings take the whole width under their words: in a column their terminal text was too small to read */
#config > .recording,
.cargo > .wide {
  grid-column: 1 / -1;
}
#config .cmd pre {
  font-size: var(--code-md);
  white-space: pre;
  overflow-x: auto;
}
.more a {
  font-weight: 700;
  color: var(--c14);
}
.crate-col .hold-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 28px;
  margin: 28px 0 0;
  font-weight: 700;
} /* where to go once it is installed */
.hold a {
  color: var(--c14);
}
/* the way down: a hatch cut in the last beam, a ladder into the hold and a lantern hung from the beam.
   Wide screens only, where the space beside the heading is free. They sit --hatch in from the column's right edge, clear of
   the intro line (which is at most 800px wide, so from 1360px the ladder is well to its right) */
@media (min-width: 1360px) {
  .below,
  .hold {
    --edge: max(var(--gut), calc(50% - 664px));
    --hatch: 60px;
  }
  .beam.last {
    position: relative;
    z-index: 5;
  }
  .beam.last::before {
    content: "";
    position: absolute;
    top: 0;
    height: calc(6 * var(--px));
    width: calc(18 * var(--px));
    right: calc(var(--edge) + var(--hatch) + 132px - 8px);
    background: var(--hold);
  }
  .beam.last::after {
    content: "";
    position: absolute;
    top: 0;
    width: calc(12 * var(--px));
    height: calc(74 * var(--px));
    right: calc(var(--edge) + var(--hatch) + 132px);
    background: url("../art/ladder.svg") 0 0 / calc(12 * var(--px)) calc(8 * var(--px)) repeat-y;
    image-rendering: pixelated;
  }
  .hold::before {
    content: "";
    position: absolute;
    z-index: 0;
    top: 0;
    width: calc(9 * var(--px));
    height: calc(15 * var(--px));
    right: calc(var(--edge) + var(--hatch) + 44px);
    background: url("../art/lantern.svg") 0 0 / 100% 100%;
    image-rendering: pixelated;
  }
  .hold::after {
    content: "";
    position: absolute;
    z-index: 0;
    top: calc(-10 * var(--px));
    width: calc(41 * var(--px));
    height: calc(41 * var(--px));
    right: calc(var(--edge) + var(--hatch) + 44px - 16 * var(--px));
    background: url("../art/glow.svg") 0 0 / 100% 100%;
    image-rendering: pixelated;
    animation: lamp 5s steps(1) infinite;
  }
}
@keyframes lamp {
  0% {
    opacity: 1;
  }
  55% {
    opacity: 0.78;
  }
  62% {
    opacity: 1;
  }
  80% {
    opacity: 0.88;
  }
}
.pile {
  position: absolute;
  right: max(var(--gut), calc(50% - 664px));
  bottom: calc(18 * var(--px) - 1 * var(--px));
  line-height: 0;
  z-index: 0;
}
.pile-s {
  right: auto;
  left: 38%;
}
/* ---- background crew (tools/backcrew.py): little neutral crewmates at odd jobs in the dark behind the copy, the way the
   navigator rides the ratlines. Each is an .act loop, so site.js pauses it while it is off screen and, with reduced motion,
   frame 0 shows. They sit behind the copy, never take a click, and are drawn in the muted grey that ink cannot be on
   this ground. To drop them, remove this block and the three .bgcrew elements in body.html. */
.bgcrew {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  line-height: 0;
}
/* the hold: a deckhand rolls a barrel to and fro along the floor. The barrel lies on its side and is seen end on, a disc
   15 art pixels across, and each frame the whole sprite moves 3 art pixels while the disc turns 22.5 degrees: 3 / 7.5
   radians, so it rolls without slipping. The walk is the same animation clock as the frames (one step a frame), and the
   sprite turns on the spot at each end. --rs is how many frames each way */
.a-bgbarrel {
  --w: 46;
  --h: 25;
  --n: 8;
  --dt: 0.24s;
  --lag: -0.7s;
  --rs: 20;
  background-image: url("../art/crew-bg-barrel.svg");
  animation:
    act calc(var(--n) * var(--dt)) steps(var(--n)) var(--lag) infinite,
    pace calc(var(--rs) * var(--dt) * 2) steps(var(--rs)) var(--lag) infinite;
}
@keyframes pace {
  0% {
    transform: translateX(0) scaleX(1);
  }
  50% {
    transform: translateX(calc(var(--rs) * 3 * var(--px))) scaleX(1);
  }
  50.01% {
    transform: translateX(calc(var(--rs) * 3 * var(--px))) scaleX(-1);
  }
  100% {
    transform: translateX(0) scaleX(-1);
  }
}
.bg-roll {
  left: var(--gut);
  bottom: calc(17 * var(--px));
} /* the cargo piles' own floor line */
/* below deck: the lookout asleep in a hammock on the blank wall above a recording, a cook mopping the beam under another */
.a-bghammock {
  --w: 56;
  --h: 32;
  --n: 12;
  --dt: 0.5s;
  --lag: -1.1s;
  background-image: url("../art/crew-bg-hammock.svg");
}
.a-bgmop {
  --w: 42;
  --h: 26;
  --n: 8;
  --dt: 0.28s;
  --lag: -0.7s;
  background-image: url("../art/crew-bg-mop.svg");
}
.bg-hammock {
  right: 60px;
  top: 36px;
}
.bg-mop {
  right: 86px;
  bottom: 0;
}
/* the walk shortens with the floor it has: the small pile stands at 38% of the width. Below 1100px the floors stack and
   have no blank wall, and below 900px the hold floor is full of cargo, so they go */
@media (max-width: 1359px) {
  .a-bgbarrel {
    --rs: 13;
  }
}
@media (max-width: 1099px) {
  .a-bgbarrel {
    --rs: 6;
  }
  .bg-hammock,
  .bg-mop {
    display: none;
  }
}
@media (max-width: 899px) {
  .bg-roll {
    display: none;
  }
}
.floor-boards {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background-image: url("../art/planks.svg");
}
.keel {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  z-index: 3;
  background-image: url("../art/keel.svg");
}

/* -------------------------------------------------------- seabed */
.seabed {
  position: relative;
  color: var(--c15);
  --ring: var(--c14);
}
.water {
  position: relative;
  overflow: hidden;
  padding: 150px 0 0;
  min-height: 720px;
  background: linear-gradient(var(--deep), var(--seabed) 55%);
}
.seabed a {
  color: var(--c14);
}
.seabed-in {
  position: relative;
  z-index: 2;
  width: min(1000px, calc(100% - 2 * var(--gut)));
  margin: 0 auto;
}
.seabed .place {
  color: var(--c14);
  font-size: 22px;
  line-height: 33px;
}
.seabed h2 {
  margin: 0 0 40px;
}
/* the three install commands (hero, hold, seabed) are shown whole: a block is as wide as its command, up to its column.
   Where the column is narrower the command wraps at a slash (the <wbr>s in the markup) and breaks inside a word only if no
   break fits. "install.sh | sh" is one nowrap span, so a lone "sh" never sits on a line of its own. None of them scrolls,
   so none is a tab stop */
.install-final {
  max-width: none;
}
.install-quick .cmd,
#install .cmd,
.install-final .cmd {
  width: max-content;
  max-width: 100%;
}
@media (min-width: 1400px) {
  .install-quick .cmd {
    max-width: calc(
      100% + 32px
    ); /* the 768px block is 26px wider than the hero's words column at 1440, and the gap before the sign is 56px */
  }
}
@media (max-width: 600px) {
  .install-quick .cmd,
  #install .cmd,
  .install-final .cmd {
    padding-top: 42px;
    padding-inline: 12px;
  } /* the copy button gets its own strip above the lines so the lines can use the whole width. 34 characters,
       "https://raw.githubusercontent.com/", fit a 360px phone on one line at these paddings */
  .install-quick .cmd pre,
  #install .cmd pre,
  .install-final .cmd pre {
    padding-right: 0;
  }
}
.install-final p {
  color: var(--c7);
  font-size: var(--t-md);
  margin: 14px 0 0;
}
.reef {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
/* the cable hangs at 9% of the width, but never over the words: the column starts at 56px (or half of what is left beside its 1000px),
   and the chain is 16px wide with the anchor's 64px under it, so the cable stops 76px short of the column's left edge */
.cable {
  position: absolute;
  left: clamp(0px, calc(max(var(--gut), (100% - 1000px) / 2) - 76px), max(24px, 9%));
  top: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  z-index: 1;
}
.cable i {
  flex: 1;
  width: calc(4 * var(--px));
  background: url("../art/chain.svg") repeat-y center top / calc(4 * var(--px)) calc(8 * var(--px));
  image-rendering: pixelated;
}
.cable img {
  margin-bottom: calc(-1 * var(--px));
}
.kelp {
  position: absolute;
  bottom: 0;
  background: url("../art/kelp.svg") 0 0 / calc(18 * var(--px)) calc(30 * var(--px));
  image-rendering: pixelated;
  animation: sway 2.6s steps(1) infinite;
}
.k1 {
  left: 30%;
}
.k2 {
  left: 33%;
  animation-delay: -0.9s;
  transform: scale(1, 0.8);
  transform-origin: bottom;
}
.k3 {
  left: 76%;
  animation-delay: -1.7s;
}
@keyframes sway {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: calc(-9 * var(--px)) 0;
  }
}
.rocks {
  position: absolute;
  bottom: calc(-2 * var(--px));
  left: 20%;
}
.starfish {
  position: absolute;
  bottom: calc(-1 * var(--px));
  left: 44%;
}
.crab {
  position: absolute;
  bottom: 0;
  left: max(70%, calc(50% + 272px)); /* clear of the chest's 256px at 50%, which the 70% reaches below 1250px */
  background: url("../art/crab.svg") 0 0 / calc(32 * var(--px)) calc(9 * var(--px));
  image-rendering: pixelated;
  animation:
    scuttle 0.9s steps(1) infinite,
    wander 22s ease-in-out infinite alternate;
}
@keyframes scuttle {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: calc(-16 * var(--px)) 0;
  }
}
@keyframes wander {
  to {
    transform: translateX(160px);
  }
}
.fish.s4 {
  bottom: 300px;
  left: 0;
  animation-delay:
    -0.2s,
    -25s;
  animation-duration: 0.7s, 90s;
}
.fish.s5 {
  bottom: 210px;
  left: 0;
  animation-delay:
    -0.4s,
    -60s;
  animation-duration: 0.9s, 110s;
}
/* two more (tools/seabed.py): three small fish that keep together, and a yellow one that swims through the lantern's light */
.fish.s6 {
  bottom: 250px;
  left: 0;
  background: url("../art/fish-school.svg") 0 0 / calc(40 * var(--px)) calc(9 * var(--px));
  animation:
    tail-school 1.1s steps(1) infinite,
    swim 120s linear infinite;
  animation-delay:
    -0.3s,
    -85s;
}
.fish.s7 {
  bottom: 105px;
  left: 0;
  background-image: url("../art/fish-glow.svg");
  animation-delay:
    -0.1s,
    -70s;
  animation-duration: 0.8s, 95s;
}
@keyframes tail-school {
  0% {
    background-position: 0 0;
  }
  50% {
    background-position: calc(-20 * var(--px)) 0;
  }
}
.reef .fish {
  opacity: 0.6;
}
.reef .fish.s7 {
  opacity: 0.95;
}
/* the second diver and the chest he has found: the lantern's light is the one warm thing in the water. The reef around it
   moved over to make room (the starfish, the third kelp) */
.chest {
  position: absolute;
  left: 50%;
  bottom: calc(-2 * var(--px));
  line-height: 0;
}
.a-chest {
  --w: 64;
  --h: 38;
  --n: 8;
  --dt: 0.55s;
  --lag: -1.1s;
  background-image: url("../art/seabed-chest.svg");
}
.diver {
  position: absolute;
  right: max(24px, 7%);
  bottom: calc(-3 * var(--px));
}
.a-diver {
  --w: 28;
  --h: 34;
  --n: 8;
  --dt: 0.55s;
  --lag: -1.6s;
  background-image: url("../art/crew-diver-loop.svg");
} /* waves, and breathes out bubbles */
.hose {
  position: absolute;
  left: calc(12 * var(--px));
  bottom: calc(18 * var(--px));
  height: 1400px;
  width: calc(2 * var(--px));
  background: repeating-linear-gradient(to top, var(--c8) 0 8px, var(--ink) 8px 12px);
}
.b4 {
  left: 86%;
}
.b5 {
  left: 88%;
  animation-delay: -6s;
  animation-duration: 15s;
}
.b6 {
  left: 85%;
  animation-delay: -11s;
  animation-duration: 21s;
}
.reef .bubble {
  bottom: 100px;
}
.sand {
  background-image: url("../art/sand.svg");
}
.foot {
  background: var(--c8);
  color: var(--c15);
  padding: 28px var(--edge) 44px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 16px 40px;
  font-size: var(--t-md);
}
.foot p {
  margin: 0;
  max-width: 60ch;
  text-wrap: balance;
}
.foot a {
  color: var(--c15);
}
.foot nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  font-weight: 700;
}

/* ---- mounted screens: every figure terminal and recording gets the crewmate's own bezel.
   One frame, all in art pixels: an ink outline (1), a cream plate (3) with a screw in each corner, then the
   ink screen. It is a stack of backgrounds on a transparent border, so the whole frame stays inside the box
   and the notched corners (clip-path, 1 art pixel) cut a clean outline. To drop the bezel, remove .mount. */
.mount {
  border: calc(4 * var(--px)) solid transparent;
  background:
    linear-gradient(var(--c8) 0 0) left calc(2 * var(--px)) top calc(2 * var(--px)) / var(--px) var(--px) no-repeat
      border-box,
    linear-gradient(var(--c8) 0 0) right calc(2 * var(--px)) top calc(2 * var(--px)) / var(--px) var(--px) no-repeat
      border-box,
    linear-gradient(var(--c8) 0 0) left calc(2 * var(--px)) bottom calc(2 * var(--px)) / var(--px) var(--px) no-repeat
      border-box,
    linear-gradient(var(--c8) 0 0) right calc(2 * var(--px)) bottom calc(2 * var(--px)) / var(--px) var(--px) no-repeat
      border-box,
    linear-gradient(var(--ink) 0 0) padding-box,
    linear-gradient(var(--c7) 0 0) center / calc(100% - 2 * var(--px)) calc(100% - 2 * var(--px)) no-repeat border-box,
    linear-gradient(var(--ink) 0 0) border-box;
  clip-path: polygon(
    var(--px) 0,
    calc(100% - var(--px)) 0,
    calc(100% - var(--px)) var(--px),
    100% var(--px),
    100% calc(100% - var(--px)),
    calc(100% - var(--px)) calc(100% - var(--px)),
    calc(100% - var(--px)) 100%,
    var(--px) 100%,
    var(--px) calc(100% - var(--px)),
    0 calc(100% - var(--px)),
    0 var(--px),
    var(--px) var(--px)
  );
}
/* the terminal rules that colour a terminal's border (night, and below deck) must not paint the bezel */
html .term.mount {
  border-color: transparent;
}
.screen {
  margin: 0;
}

/* ---- the bottle at the waterline is an agent: a tag beside it says which state (glyph and word), and pressing it
   looks at it (unseen becomes idle) and again sends it off. site.js, bottleTag(). To drop it, remove this block,
   bottleTag() and the button in body.html (the old picture was a plain image in .flotsam). */
.bottle {
  position: absolute;
  z-index: 4;
  left: calc(17% - 2 * var(--px));
  top: calc(14px - 10 * var(--px) * 0.7 - 2 * var(--px));
  padding: calc(2 * var(--px)); /* a 44 by 56 target around the 28 by 40 bottle */
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  line-height: 0;
  cursor: pointer;
  animation: bob 5s steps(2) infinite;
}
.bottle::before {
  content: "";
  position: absolute;
  inset: -4px;
}
@media (pointer: coarse) and (max-width: 600px) {
  .bottle::before {
    inset: -8px;
  } /* the bottle is smaller on a phone: 49 by 58 with its target */
}
.bottle .tag {
  position: absolute;
  left: 100%;
  top: 50%;
  margin-top: -11px;
  background: var(--ink);
  color: var(--c15);
  font: var(--code-sm)/22px var(--code);
  padding: 0 8px;
  white-space: nowrap;
  box-shadow: 0 0 0 2px var(--ink);
}
.bottle .tag .g {
  font-style: normal;
}
.bottle .tag b {
  font-weight: 400;
}
.bottle:focus-visible .tag {
  box-shadow:
    0 0 0 2px var(--ink),
    0 0 0 4px var(--c14);
}
@media (hover: hover) {
  .bottle:hover .tag {
    box-shadow:
      0 0 0 2px var(--ink),
      0 0 0 4px var(--c14);
  }
}

/* ---- deck ledger: the titles are links into the docs, a row you point at or Tab to turns into the selected row
   (ship's own cyan, ink text), and the navigator rides the ratlines up to it. site.js adds .sel to the row and
   sets --dy and --n on the climber. To drop all of it, remove this block and ledgerClimb() in site.js. */
.works h4 a {
  color: inherit;
  text-decoration: none;
}
.works h4 a::after {
  content: "›";
  margin-left: 0.4em;
  color: var(--chev, var(--sea));
}
@media (hover: hover) {
  .works h4 a:hover {
    text-decoration: underline;
    text-decoration-thickness: 2px;
  }
}
.works li.sel {
  background: var(--dash), var(--c6);
  box-shadow:
    calc(-3.5 * var(--px)) 0 0 var(--c6),
    calc(3.5 * var(--px)) 0 0 var(--c6);
  color: var(--ink);
}
.works li.sel h4,
.works li.sel h4 a::after,
.works li.sel p,
.works li.sel code {
  color: var(--ink);
} /* every word in the row, in both modes: night colours the page's text cream, which the cyan band cannot take */
.works li.sel h4::before {
  background: var(--ink);
}
/* the global focus ring is cyan at night and would vanish on the cyan band: ring the whole row in ink instead */
.works li:has(a:focus-visible) {
  box-shadow:
    inset 0 0 0 3px var(--ink),
    calc(-3.5 * var(--px)) 0 0 var(--c6),
    calc(3.5 * var(--px)) 0 0 var(--c6);
}
.works h4 a:focus-visible {
  outline: 0;
  text-decoration: underline;
  text-decoration-thickness: 2px;
}
.climber {
  display: none;
}
/* a pointer is what sends it climbing; with touch alone it would only stand at the mast beside the captain */
@media (min-width: 900px) and (any-hover: hover) {
  .climber {
    display: block;
    position: absolute;
    right: calc(max(var(--gut), 50% - 664px) + 4 * var(--px));
    bottom: calc(29 * var(--px));
    z-index: 1;
    pointer-events: none;
    transform: translateY(var(--dy, 0px));
    transition: transform calc(var(--n, 1) * 70ms) steps(var(--n, 1));
  }
  .climber .act {
    transform: scaleX(-1);
  }
}

/* ---- the footer: the big mark (one css pixel per art pixel, so it is crisp) and the licence on the left, the
   links on the right. To drop it, remove this block and the mark in body.html. */
.foot {
  align-items: center;
  justify-content: flex-start;
}
.foot .mark {
  flex: none;
  width: 72px;
  height: 72px;
}
.foot p {
  margin-right: auto;
}

/* ------------------------------------------------ narrower screens */
/* between 900 and 1299px the headline's three locked lines run past the text column, right where the first cloud
   drifts, and below 1020px the first gull crosses the second line: lift the cloud into the open band above the headline
   and let that one gull sit out the narrow widths */
@media (min-width: 900px) and (max-width: 1299px) {
  .cloud.a {
    top: 12px;
    left: 38%;
  }
}
@media (min-width: 900px) and (max-width: 1019px) {
  .g1 {
    display: none;
  }
}
@media (max-width: 1099px) {
  .floor {
    grid-template-columns: calc(84 * var(--px)) minmax(0, 1fr);
  }
  .floor-fig,
  .floor .recording {
    grid-column: 1 / -1;
  }
  .cargo,
  #config {
    grid-template-columns: minmax(0, 1fr);
  }
  #config > .term {
    grid-column: 1;
    grid-row: auto;
  }
  .hold-head {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 1199px) {
  #config {
    grid-template-columns: minmax(0, 1fr);
  } /* the scene is 336px wide, and the words column of the two-column config is under that below 1200px */
  #config > .term {
    grid-column: 1;
    grid-row: auto;
  }
}
@media (max-width: 1249px) {
  .flotsam .dinghy,
  .flotsam .f5,
  .flotsam .f6 {
    display: none;
  }
  .dinghy-flow {
    display: block;
    position: relative;
    left: auto;
    top: auto;
    justify-self: end;
    margin: -16px 0 -8px;
    z-index: 1;
  }
}
@media (max-width: 899px) {
  /* the lantern diver and his chest are 256px wide and the reef is laid out in percentages: on a narrow screen they take the
     middle of the floor, the rocks, the starfish, the crab and two of the kelp step aside, and the third (first) kelp stands by the anchor */
  .chest {
    left: 22%;
  }
  .reef .rocks,
  .reef .starfish,
  .reef .crab,
  .reef .k2,
  .reef .k3 {
    display: none;
  }
  .k1 {
    left: 12%;
  }
  .cable {
    left: 8px;
  }
  .seabed-in {
    padding-left: 28px;
  } /* the page margin is 28px or less here, so the words stand off to the cable's right */
}
@media (max-width: 760px) {
  .floor {
    grid-template-columns: minmax(0, 1fr);
  } /* the rooms are 84 art pixels wide: beside them the words would be under 300px */
}
@media (max-width: 1099px) {
  .crew > li.prop {
    display: none;
  } /* five crew and two prop groups need about 1090px */
  .works li {
    grid-template-columns: 330px minmax(0, 1fr);
  } /* the ledger's words need the room more than the longest title needs its link marker on one line */
}
@media (max-width: 899px) {
  .hero {
    grid-template-columns: minmax(
      0,
      1fr
    ); /* not 1fr: the install block's unwrapped commands would hold the column open past the screen */
    gap: 0;
  }
  .hero-text {
    padding-bottom: 40px;
  }
  .hero h1 {
    font-size: 44px;
    line-height: 55px;
  }
  .rig {
    min-height: 0;
    padding-bottom: calc(18 * var(--px));
  }
  .nest {
    top: 0;
  }
  .mast {
    top: calc(48 * var(--px));
    bottom: calc(18 * var(--px));
  }
  .ratlines {
    display: none;
  }
  .mast-foot {
    display: block;
    position: absolute;
    right: calc(14 * var(--px));
    bottom: 0;
    width: calc(32 * var(--px));
    height: calc(18 * var(--px));
    background: url("../art/planks.svg") 0 0 / calc(48 * var(--px)) calc(18 * var(--px));
    image-rendering: pixelated;
  }
  .mast-foot::before {
    content: "";
    position: absolute;
    left: calc(5 * var(--px));
    top: calc(-8 * var(--px));
    width: calc(14 * var(--px));
    height: calc(8 * var(--px));
    background: url("../art/mast-base.svg") 0 0 / 100% 100%;
    image-rendering: pixelated;
  }
  .board {
    position: relative;
    top: auto;
    margin-top: calc(18 * var(--px));
    width: calc(100% - 28 * var(--px));
    max-width: 300px;
  }
  .cloud.a {
    top: 12px;
    left: 52%;
  } /* clear of the headline, which starts 110px down */
  .cloud.b,
  .g2,
  .g3 {
    display: none;
  }
  .cloud.c {
    top: 20px;
    left: 3%;
  }
  .g1 {
    top: 50px;
    left: 31%;
  }
  .deck-in {
    padding-right: 0;
  }
  .deck > .mast,
  .deck > .ratlines {
    display: none;
  }
  .works li {
    grid-template-columns: minmax(0, 1fr);
    gap: 6px;
  }
  .notyet ul {
    columns: 1;
  }
  .stage {
    padding-right: 0;
  }
  .water-in {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
  }
  .seabed .place {
    display: inline;
  }
}
@media (max-width: 600px) {
  .hero h1 {
    font-size: 33px;
    line-height: 44px;
  }
  .lede {
    font-size: var(--t-lg);
  }
  .hero-text > p:not(.lede) {
    font-size: var(--t-base);
  }
  .wg-head h3 {
    font-size: 22px;
    line-height: 33px;
  }
  .sky {
    padding-top: 76px;
  }
  .cloud.a {
    top: 6px;
  }
  .nest {
    top: 0;
  }
  .board {
    margin-top: calc(34 * var(--px));
  } /* the board is as wide as the sky here, so it sits under the nest, not beside it */
  .stagewrap {
    margin-top: 28px;
  }
  .stage {
    width: 100%;
  } /* the decks and the cabin run to the screen's edges */
  .rail-far {
    display: none;
  }
  .crew {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0;
    justify-items: center;
  }
  .crew > li {
    margin-bottom: 0;
    padding-bottom: calc(18 * var(--px));
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: flex-end;
    background: url("../art/planks.svg") 0 100% / calc(48 * var(--px)) calc(18 * var(--px)) repeat-x;
    image-rendering: pixelated;
  }
  .crew > li.prop:first-child {
    display: flex;
  }
  /* the crew stands on two decks, so the lower row is the cabin under the upper deck's planks */
  .crew > li:nth-child(n + 4) {
    padding-top: calc(10 * var(--px));
    background:
      url("../art/planks.svg") 0 100% / calc(48 * var(--px)) calc(18 * var(--px)) repeat-x,
      url("../art/wall.svg") 0 0 / calc(32 * var(--px)) calc(22 * var(--px)),
      var(--wall);
  }
  .planks.tile {
    display: none;
  }
  .plate {
    top: calc(100% - 17 * var(--px) + 6px);
    padding: 1px 8px 2px;
  }
  .plate .role {
    line-height: 18px;
  }
  .floor {
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
    padding: 36px 0 40px;
  }
  .water-copy p {
    font-size: var(--t-base);
  }
  .dg {
    padding: 12px;
    font-size: var(--code-sm);
  }
  .dg-wins {
    gap: 8px;
  }
  .dg-win {
    padding: 4px 6px 6px;
  }
  .ui {
    grid-template-columns: minmax(0, 1fr);
  }
  .ui-side {
    border-right: 0;
    border-bottom: 2px solid var(--c8);
  }
  .mock {
    grid-template-columns: minmax(0, 1fr);
  }
  .mock .side {
    border-right: 0;
    border-bottom: 1px solid var(--c8);
  }
  .mock .pane {
    white-space: pre-wrap;
  }
  .keys,
  .keys tbody,
  .keys tr,
  .keys th,
  .keys td {
    display: block;
    width: auto;
  }
  .keys tr {
    padding: 8px 0 9px;
    border-bottom: 1px solid #3a3650;
  }
  .keys th,
  .keys td {
    padding: 0;
    border: 0;
    white-space: normal;
  }
  .keys th {
    margin-bottom: 4px;
  }
  .water {
    padding-top: 90px;
  }
  .water-in h2 .nb {
    white-space: normal;
  } /* 17 pixel-face characters are wider than a phone's column */
  .seabed h2 {
    font-size: 44px;
    line-height: 55px;
  }
  .diver {
    right: 12px;
  }
  .hose {
    height: 220px;
  }
  .cmd pre {
    font-size: var(--code-sm);
  }
}

/* key and command chips in the landing's prose stay on one line: they are all short, and `alt-` / `a` reads as two keys */
p code {
  white-space: nowrap;
}

/* a recording this build has no take of: the bezel holds a crewmate and a line saying so (site.js, noTake) */
.screen.no-take {
  aspect-ratio: 16 / 9;
  display: grid;
}
.no-take-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: var(--c7); /* the rooms' wall, so the crewmate's ink outline shows in both modes */
  color: var(--c0);
  padding: 24px;
  text-align: center;
  font-size: var(--t-sm);
}
.no-take-card img {
  width: calc(20 * var(--px));
  height: auto;
}
.no-take-card p {
  color: var(--c0); /* the hold's .crate-col p is wall coloured, which would vanish on the card */
  font-size: var(--t-sm);
  margin: 0;
  max-width: 34ch;
}

/* the footer's first row: the latest from the captain's log (the blog), above the licence and links */
.foot .log {
  flex: 1 1 100%;
  max-width: none;
  margin: 0 0 8px;
  padding-bottom: 20px;
  border-bottom: 2px dashed color-mix(in srgb, var(--c15) 35%, transparent);
  text-wrap: pretty;
}
.foot .log .pixel {
  color: var(--c15); /* c14 fell short of 4.5:1 on the footer's c8 */
  margin-right: 10px;
}

/* the hold's recordings, each under its own words, and a second row of three: agents, mouse and saved tabs */
.crate-col .recording {
  margin: 8px 0 20px;
}
.crate-col .recording figcaption {
  font-size: var(--t-sm);
}
.cargo-more {
  grid-template-columns: minmax(0, 1fr);
  margin-top: 72px;
  padding-top: 56px;
  border-top: 2px dashed var(--c8);
}
/* agents, mouse and saved tabs: one row each, the words on the left and the recording on two thirds of the width */
@media (min-width: 1200px) {
  .cargo-more > .crate-col {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    column-gap: 64px;
    align-items: start;
  }
  .cargo-more > .crate-col > * {
    grid-column: 1;
  }
  .cargo-more > .crate-col > .recording {
    grid-column: 2;
    grid-row: 1 / span 4;
    margin: 0;
  }
}
.floor-fig .sketch[hidden] {
  display: none;
}
