/* Lifeboats — a weathered sea chart.
 *
 * The whole theme is drawn: gradients for parchment, planking, water and sand,
 * and a handful of inline-SVG data URIs for the palm and the skull. No image
 * files, no web fonts, no network requests. Everything below is fixed to the
 * one parchment palette rather than following the system light/dark setting,
 * so the chart reads the same wherever it is opened.
 */

:root {
  color-scheme: light only;

  --sans: ui-sans-serif, system-ui, "Segoe UI", sans-serif;
  --display: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
    "Times New Roman", serif;

  /* Parchment, light to dark. */
  --paper: #f0e2c2;
  --paper-2: #e6d4ae;
  --paper-3: #d7c095;
  --paper-edge: #b39c6f;

  /* Ink, in the three weights the chart needs. */
  --ink: #33261a;
  --ink-2: #5d4a33;
  --ink-3: #85704f;

  /* Brass fittings — every important edge and every call to action. */
  --brass: #b8892c;
  --brass-lit: #e3bb63;
  --brass-dark: #6d4f14;

  --rope: #a8834e;
  --rope-dark: #795c33;

  /* Open water, shallow to deep. */
  --sea: #0e3f49;
  --sea-2: #145b64;
  --sea-3: #1e838a;
  --foam: rgba(226, 244, 240, 0.5);

  --sand: #ddc286;
  --sand-2: #c7a763;

  /* Blood-red, for danger and for leaving. */
  --blood: #8f2f24;
  --blood-lit: #c2503f;

  /* Player colours: signal-flag versions of the eight names, chosen to hold
     their identity both on parchment and on open water. */
  --red: #b5342c;
  --orange: #cd7828;
  --yellow: #d6b437;
  /* Darker than the other hulls look like they want to be, because it carries
     light text: at #4b8a4b that pairing came to 3.55:1, under the 4.5:1 a
     12.5px bold label needs. This reads 5.0:1. */
  --green: #3a6c3a;
  --blue: #37669c;
  --white: #ece0c6;
  --purple: #77569b;
  --black: #2a2723;

  /* Teal is reserved for leaks; no player ever wears it. */
  --leak: #35c6c0;
  --leak-edge: #0a4f4c;
  --leak-ink: #032b29;
}

/* The app hides and shows panels with the `hidden` attribute, and a class that
   sets `display` would otherwise beat the user-agent's `[hidden]` rule and
   leave the panel on screen — as `.confirm { display: flex }` did to the leave
   confirmation. This keeps `hidden` meaning hidden whatever else is declared. */
[hidden] { display: none !important; }

/* ---------------------------------------------------------------- page ---- */

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--sans);
  color: var(--ink);
  /* Deep water beyond the edges of the chart. */
  background:
    radial-gradient(120% 80% at 50% -20%, #1a6068 0%, transparent 60%),
    linear-gradient(180deg, #0d3a43 0%, #072228 100%);
  background-attachment: fixed;
}

/* Wide enough for the board and the log rail side by side. The two pre-game
   screens do not want this much width, so they cap themselves — see .narrow. */
main {
  max-width: 82rem;
  margin: 1rem auto 1.5rem;
  padding: 1rem 1.25rem 1.5rem;
  color: var(--ink);
  /* Parchment: a base wash, then blotches and fibre laid over it. The
     percentages are arbitrary — they only need to be uneven. */
  background:
    radial-gradient(60% 45% at 12% 8%, rgba(150, 116, 66, 0.16), transparent 70%),
    radial-gradient(50% 40% at 88% 22%, rgba(122, 92, 50, 0.14), transparent 70%),
    radial-gradient(70% 50% at 30% 96%, rgba(140, 108, 60, 0.18), transparent 70%),
    radial-gradient(40% 30% at 72% 70%, rgba(160, 128, 78, 0.12), transparent 70%),
    repeating-linear-gradient(96deg,
      rgba(120, 92, 52, 0.05) 0 2px, transparent 2px 7px),
    linear-gradient(160deg, var(--paper) 0%, var(--paper-2) 55%, var(--paper-3) 100%);
  border: 1px solid var(--paper-edge);
  border-radius: 0.5rem;
  box-shadow:
    0 1.5rem 3rem rgba(0, 0, 0, 0.45),
    inset 0 0 4rem rgba(115, 86, 46, 0.18);
}

/* ------------------------------------------------------------ masthead ---- */

.masthead {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding-bottom: 0.5rem;
  margin-bottom: 0.7rem;
  /* A length of rope under the title, in place of a rule. */
  border-bottom: 5px solid transparent;
  border-image: repeating-linear-gradient(48deg,
    var(--rope) 0 5px, var(--rope-dark) 5px 10px) 5;
}

/* Seat and the way out, pushed to the far end of the title bar so neither
   costs a line of its own. */
.masthead-side {
  margin-left: auto;
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  font-family: var(--display);
  font-size: 0.95rem;
  color: var(--ink-2);
  text-align: right;
}

/* Your colour, worn rather than stated. It is the one fact you need at a
   glance — every boat, pip and vote chip on the page is keyed to it — so it
   is a planked board in your own paint, brass-edged and unmissable. */
.seat-badge {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.28rem 0.7rem;
  border-radius: 0.3rem;
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #1a1408;
  background-color: var(--hull, transparent);
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, 0.32), rgba(0, 0, 0, 0.2)),
    repeating-linear-gradient(180deg,
      rgba(0, 0, 0, 0.11) 0 1px, transparent 1px 7px);
  border: 1px solid var(--brass-dark);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 0 0 1px rgba(255, 248, 226, 0.55),
    0 1px 3px rgba(40, 26, 8, 0.4);
}
.seat-badge:empty { display: none; }
.seat-badge.dark { color: #f8efd9; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5); }
/* "You are" is the quiet half; the colour is the loud half. */
.seat-badge .badge-lead { font-weight: 400; opacity: 0.75; letter-spacing: 0.06em; }
.seat-badge .badge-color { font-size: 0.95rem; letter-spacing: 0.05em; }

/* The seat is worth a beat of attention when the game starts waiting on you
   specifically, rather than on the table as a whole. */
.seat-badge.yours {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 0 0 2px var(--brass-lit),
    0 0 0 3px rgba(40, 26, 8, 0.5),
    0 1px 4px rgba(40, 26, 8, 0.45);
}

/* Who the table is waiting on, in their own colour, inside the ribbon. Only
   set during the phases that genuinely wait on one player. */
.turn-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-right: 0.5rem;
  padding: 0.1rem 0.45rem;
  border-radius: 0.25rem;
  font-size: 0.78rem;
  font-weight: 700;
  color: #1a1408;
  background-color: var(--hull, transparent);
  border: 1px solid rgba(0, 0, 0, 0.45);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4);
}
.turn-chip.dark { color: #f6ecd6; }

.compass { width: 2.4rem; height: 2.4rem; flex: none; }
.rose-ring { fill: none; stroke: var(--brass-dark); stroke-width: 2; opacity: 0.75; }
.rose-ring.thin { stroke-width: 1; opacity: 0.45; }
.rose-point.light { fill: var(--brass-lit); }
.rose-point.dark { fill: var(--brass-dark); }
.rose-point.faint { fill: var(--ink-2); opacity: 0.35; }
.rose-hub { fill: var(--brass-dark); }

h1 {
  font-family: var(--display);
  font-size: 1.55rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  margin: 0;
  color: var(--ink);
  text-shadow: 0 1px 0 rgba(255, 245, 220, 0.55);
}

.tagline {
  font-family: var(--display);
  font-style: italic;
  font-size: 0.82rem;
  color: var(--ink-2);
  margin: 0.05rem 0 0;
}

h2 {
  font-family: var(--display);
  font-size: 1.2rem;
  letter-spacing: 0.03em;
  margin: 0 0 0.4rem;
}

/* --------------------------------------------------------- play layout ---- */

/* Board and hand on the left, the round log in a rail beside them. Flex, not
   grid: a hidden rail then costs nothing and the board takes the full width,
   which is what the lobby's board preview wants. */
.play {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}
.play-main { flex: 1; min-width: 0; }

/* The log is history, not something you act on, so it gets the narrow side
   and scrolls within itself. Sticky, so it stays put if the page does scroll
   — which is the whole point of moving it out of the column. */
#log.rail {
  flex: 0 0 21rem;
  position: sticky;
  top: 0.75rem;
  /* Tall enough to fill the screen below the title bar and the ribbon, and no
     taller: the rail is the only thing here that grows without limit, so if it
     may overhang the viewport it becomes the reason the page scrolls. Roughly
     13rem of chrome sits above it. Content past this scrolls inside the rail. */
  box-sizing: border-box;
  max-height: calc(100vh - 13rem);
  min-height: 10rem;
  overflow-y: auto;
  padding: 0.6rem 0.8rem 0.8rem;
  border-radius: 0.35rem;
  border: 1px solid rgba(121, 92, 51, 0.45);
  background: linear-gradient(160deg, rgba(255, 251, 238, 0.55), rgba(218, 199, 162, 0.45));
  box-shadow: inset 0 0 0 3px rgba(255, 250, 235, 0.3);
}
#log.rail > summary {
  cursor: pointer;
  font-family: var(--display);
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--brass-dark);
  padding-bottom: 0.35rem;
  border-bottom: 1px solid rgba(121, 92, 51, 0.4);
}
#log.rail[open] > summary { margin-bottom: 0.5rem; }

/* The legend earns its space once and then never again. */
.legend { margin-top: 0.6rem; }
.legend > summary {
  cursor: pointer;
  font-size: 0.82rem;
  color: var(--brass-dark);
  width: fit-content;
}
.legend .note { margin-top: 0.4rem; }

/* The two pre-game screens read as columns of prose and should not stretch to
   the width the board needs — nor should the parchment they sit on, which
   would otherwise be mostly empty. `show()` puts the screen on the body. */
.narrow { max-width: 52rem; }
body[data-screen="home"] main,
body[data-screen="lobby"] main { max-width: 56rem; }

/* -------------------------------------------------------------- modals ---- */

/* Confirming a leave and reading the final standings both used to sit in the
   flow and shove the board down the page. As dialogs they cost no layout at
   all. */
dialog.modal {
  max-width: min(34rem, calc(100vw - 2rem));
  padding: 1rem 1.15rem;
  color: var(--ink);
  border: 1px solid var(--rope-dark);
  border-radius: 0.4rem;
  background:
    repeating-linear-gradient(0deg,
      transparent 0 1.55rem, rgba(121, 92, 51, 0.13) 1.55rem 1.6rem),
    linear-gradient(160deg, var(--paper) 0%, var(--paper-2) 60%, var(--paper-3) 100%);
  box-shadow:
    inset 0 0 0 3px rgba(255, 250, 235, 0.4),
    0 1.5rem 3rem rgba(0, 0, 0, 0.5);
}
dialog.modal::backdrop { background: rgba(6, 28, 33, 0.62); }
.modal-actions { margin-top: 0.9rem; justify-content: flex-end; }

/* Leaving is the destructive one, so its panel is the red one. */
#leave-confirm { border-color: #5e1a12; }
#leave-confirm p { margin: 0; font-size: 0.92rem; }

#status {
  min-height: 1.5em;
  /* Flex so the turn chip sits on the ribbon's centre line rather than the
     text baseline. `#status:empty` outranks this and still hides it. */
  display: flex;
  align-items: center;
  font-weight: 600;
  font-size: 0.95rem;
  color: #f6ead0;
  margin: 0 0 0.6rem;
  padding: 0.5rem 1.5rem;
  background: linear-gradient(180deg, var(--sea-2), var(--sea));
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  border-bottom: 1px solid rgba(0, 0, 0, 0.35);
  /* A ribbon with notched ends. */
  clip-path: polygon(0 0, 100% 0, calc(100% - 0.85rem) 50%, 100% 100%,
    0 100%, 0.85rem 50%);
}
#status:empty { display: none; }

#seat:empty { display: none; }

/* The sound switch. It says which way it is set rather than which way it
   would go, so the icon and the word agree with each other. */
.sound-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.6rem;
  font-size: 0.78rem;
}
.sound-toggle .sound-icon {
  width: 0.95rem; height: 0.95rem;
  background: no-repeat center / contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%2333261a' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 9.5h3.5L12 5v14l-4.5-4.5H4z' fill='%2333261a'/%3E%3Cpath d='M15.5 9a4 4 0 0 1 0 6'/%3E%3Cpath d='M18.5 6.2a8 8 0 0 1 0 11.6'/%3E%3C/g%3E%3C/svg%3E");
}
/* Muted: the waves come off and a bar goes through it, so the two states are
   told apart by shape and not only by the word beside them. */
/* Muted is the quieter state but not a quieter label: `--ink-3` measured 3.7:1
   on the button's parchment, under the 4.5:1 a 12.5px label needs. The icon
   carries "off"; the word stays as readable as it was. */
.sound-toggle.off { color: var(--ink-2); }
.sound-toggle.off .sound-icon {
  opacity: 0.75;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%2385704f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 9.5h3.5L12 5v14l-4.5-4.5H4z' fill='%2385704f'/%3E%3Cpath d='M16 9.5l5 5M21 9.5l-5 5'/%3E%3C/g%3E%3C/svg%3E");
}

.note { font-size: 0.85rem; color: var(--ink-2); margin-top: 0.75rem; }
.hint { font-size: 0.85rem; color: var(--ink-2); margin: 0 0 0.75rem; }
.row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }

/* ------------------------------------------------------------- controls ---- */

input, select {
  font: inherit;
  font-size: 0.9rem;
  color: var(--ink);
  background: rgba(255, 250, 236, 0.75);
  border: 1px solid var(--rope-dark);
  border-radius: 0.25rem;
  padding: 0.3rem 0.5rem;
}
input:focus-visible, select:focus-visible, button:focus-visible {
  outline: 2px solid var(--brass-dark);
  outline-offset: 2px;
}

button {
  font: inherit;
  font-size: 0.85rem;
  color: var(--ink);
  cursor: pointer;
  padding: 0.32rem 0.75rem;
  border-radius: 0.25rem;
  border: 1px solid var(--rope-dark);
  background: linear-gradient(180deg, rgba(255, 250, 236, 0.9), rgba(222, 205, 170, 0.9));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
/* Scoped away from the two kinds of button that carry a background of their
   own. `button:hover:not(:disabled)` outranks a bare `.card`, so unscoped it
   replaced a boat's hull with this cream — leaving the light text a dark hull
   is given sitting on a pale background at about 1:1. A link button lost its
   flat look the same way. */
button:not(.card, .link-button):hover:not(:disabled) {
  background: linear-gradient(180deg, #fffaf0, #e8d8b4);
}
button:active:not(:disabled) { transform: translateY(1px); }

/* A brass plaque: the one thing on any screen you are meant to press. */
button.primary {
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 0.4rem 1.1rem;
  color: #2a1c05;
  border: 1px solid var(--brass-dark);
  background: linear-gradient(180deg, var(--brass-lit) 0%, var(--brass) 55%, #96701e 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 244, 205, 0.85),
    inset 0 -2px 4px rgba(80, 55, 8, 0.4),
    0 1px 2px rgba(0, 0, 0, 0.3);
}
button.primary:hover:not(:disabled) {
  background: linear-gradient(180deg, #f2d284 0%, #c79830 55%, #a67c22 100%);
}
button:disabled { opacity: 0.45; cursor: default; box-shadow: none; }

#code {
  text-transform: uppercase;
  letter-spacing: 0.14em;
  width: 7rem;
  font-family: var(--display);
  font-weight: 700;
}

.link-button {
  background: none; border: none; padding: 0; box-shadow: none;
  color: var(--brass-dark); text-decoration: underline;
  cursor: pointer; font: inherit; font-size: inherit;
}
.link-button:hover { background: none; color: var(--blood); }

.error {
  margin-top: 1rem; padding: 0.6rem 0.8rem; border-radius: 0.3rem;
  color: #fbeae6;
  background: linear-gradient(180deg, var(--blood-lit), var(--blood));
  border: 1px solid #5e1a12;
}

/* ------------------------------------------------------------ home cards --- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
}

/* A notice nailed to the harbour board. `.card` is also the class on a playing
   card, so the panel rules are scoped to a direct child of `.cards` — which
   also outranks the bare `.card` rule further down and has to undo the type
   styling that rule sets. */
.cards > .card {
  position: relative;
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
  padding: 1.1rem 1.15rem;
  border: 1px solid var(--rope-dark);
  border-radius: 0.3rem;
  background:
    repeating-linear-gradient(92deg, rgba(120, 92, 52, 0.04) 0 2px, transparent 2px 6px),
    linear-gradient(150deg, rgba(255, 251, 238, 0.75), rgba(226, 209, 172, 0.55));
  box-shadow:
    inset 0 0 0 3px rgba(255, 250, 235, 0.4),
    0 2px 6px rgba(60, 42, 18, 0.18);
}
/* Four nail heads, one per corner. */
.cards > .card::before,
.cards > .card::after {
  content: "";
  position: absolute;
  top: 0.45rem;
  width: 0.4rem; height: 0.4rem;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--brass-lit), var(--brass-dark));
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
}
.cards > .card::before { left: 0.45rem; }
.cards > .card::after { right: 0.45rem; }

/* --------------------------------------------------------------- lobby ---- */

.code-line { margin: 0 0 0.75rem; }
.code-line strong {
  font-family: var(--display);
  font-size: 1.6rem;
  letter-spacing: 0.22em;
  color: var(--brass-dark);
  text-shadow: 0 1px 0 rgba(255, 248, 226, 0.7);
}
.share { margin-bottom: 1rem; }
.share #link { flex: 1; min-width: 14rem; }
.count { font-weight: 600; margin: 0 0 0.5rem; }
#seat-list { list-style: none; padding: 0; margin: 0 0 0.5rem; display: grid; gap: 0.3rem; }

/* The crew manifest. */
.seat-row {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.6rem; border-radius: 0.3rem;
  background: linear-gradient(180deg, rgba(255, 250, 236, 0.6), rgba(216, 197, 160, 0.45));
  border: 1px solid rgba(121, 92, 51, 0.35);
  border-left: 4px solid var(--hull, var(--rope));
}
.seat-row.pending { opacity: 0.6; }
.seat-name { text-transform: capitalize; flex: 1; font-weight: 600; }
.seat-state { font-size: 0.8rem; color: var(--ink-2); font-style: italic; }

button.danger {
  font-weight: 700;
  color: #fff3ee;
  border: 1px solid #4d150f;
  background: linear-gradient(180deg, var(--blood-lit), var(--blood));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
button.danger:hover:not(:disabled) {
  background: linear-gradient(180deg, #d15f4c, #9c352a);
}

/* ----------------------------------------------------------------- sea ---- */

#table { overflow-x: auto; padding: 0.25rem 0 0.5rem; }

/* Open water under the whole track. The horizontal bands are swell; the two
   radial washes are light on the surface. */
#sea {
  position: relative;
  /* A stacking context of its own, so the flotsam's negative z-index is
     measured against this panel: without it the wreckage would be painted
     behind the water rather than in it, and disappear. */
  isolation: isolate;
  min-width: 36rem;
  /* Deeper at the foot: crew adrift astern of a boat on the start row hang
     below its hull, and this is the water they hang in. */
  padding: 0.6rem 0.6rem 1.1rem;
  border-radius: 0.4rem;
  background:
    radial-gradient(90% 60% at 50% 0%, rgba(120, 220, 210, 0.16), transparent 65%),
    radial-gradient(60% 40% at 15% 100%, rgba(10, 40, 46, 0.5), transparent 70%),
    linear-gradient(180deg, var(--sea-3) 0%, var(--sea-2) 45%, var(--sea) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(0, 0, 0, 0.35),
    inset 0 2px 14px rgba(0, 0, 0, 0.4),
    0 2px 8px rgba(0, 0, 0, 0.3);
}

/* Swell and foam, drifting across the water. Kept on a pseudo-element so the
   animation never touches the boats laid over it. */
#sea::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background:
    repeating-linear-gradient(97deg,
      transparent 0 26px, var(--foam) 26px 27px, transparent 27px 62px),
    repeating-linear-gradient(84deg,
      transparent 0 40px, rgba(255, 255, 255, 0.14) 40px 41px, transparent 41px 96px),
    repeating-linear-gradient(180deg,
      rgba(255, 255, 255, 0.07) 0 1px, transparent 1px 14px);
  opacity: 0.55;
  animation: drift 26s linear infinite;
}

@keyframes drift {
  from { background-position: 0 0, 0 0, 0 0; }
  to { background-position: 620px 0, -960px 0, 0 140px; }
}

/* What is left of the ship these boats came off: her planking, a barrel and a
 * crate out of the hold, and the two things that turned up for the rest of
 * the crew. All of it drawn, like everything else on the chart.
 *
 * Behind the fleet rather than over it — z-index -1 puts a positioned child
 * above the sea's own background and under everything laid out in it, which
 * is where flotsam belongs. The swell keeps its place above this, so the foam
 * washes over the wreckage rather than under it.
 *
 * Positions are in per cent so the field spreads with the board, and they are
 * chosen off the lane centres: a boat takes the middle of its column and this
 * lies in the water between them. */
#sea::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background-repeat: no-repeat;
  /* All of it below the finish line: the islands are laid out over this and
     anything in the top third of the panel is simply behind sand. */
  background-position:
    7% 40%, 89% 36%, 45% 63%, 26% 78%, 70% 90%, 17% 55%, 82% 62%;
  background-size:
    3.9rem 0.91rem, 2.6rem 2.6rem, 2.6rem 2.6rem,
    1.45rem 1.45rem, 1.2rem 1.2rem,
    1.9rem 4.1rem, 1.9rem 4.1rem;
  background-image:
    /* A length of her planking, floating flat. */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 14'%3E%3Crect x='1' y='2.5' width='58' height='9' rx='2' fill='%233a2a17'/%3E%3Crect x='1' y='2.5' width='58' height='2.6' rx='1.3' fill='%235d4728'/%3E%3Ccircle cx='13' cy='7' r='1' fill='%231a1208'/%3E%3Ccircle cx='47' cy='7' r='1' fill='%231a1208'/%3E%3C/svg%3E"),
    /* Two more, adrift at whatever angle the sea left them. */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg transform='rotate(-29 20 20)'%3E%3Crect x='2' y='16.5' width='36' height='7' rx='1.8' fill='%23362713'/%3E%3Crect x='2' y='16.5' width='36' height='2.2' rx='1.1' fill='%23573f21'/%3E%3C/g%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg transform='rotate(52 20 20)'%3E%3Crect x='5' y='17' width='30' height='6' rx='1.6' fill='%2331240f'/%3E%3Crect x='5' y='17' width='30' height='2' rx='1' fill='%23503a1d'/%3E%3C/g%3E%3C/svg%3E"),
    /* A barrel, and a crate out of the hold — both seen end-on from above. */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='10' fill='%234a3418' stroke='%231a1208' stroke-width='1.6'/%3E%3Ccircle cx='12' cy='12' r='5.6' fill='none' stroke='%236e5129' stroke-width='1.4'/%3E%3Cpath d='M12 2.4v19.2M2.4 12h19.2' stroke='%23281c0e' stroke-width='0.9'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='2.2' y='2.2' width='15.6' height='15.6' rx='1.2' fill='%23473115' stroke='%231a1208' stroke-width='1.5'/%3E%3Cpath d='M2.8 2.8l14.4 14.4M17.2 2.8L2.8 17.2' stroke='%236b4f27' stroke-width='1.1'/%3E%3C/svg%3E"),
    /* And the sharks. The body is a shadow just under the surface and stays
       one — dark on dark water, it is meant to be half-seen. The fin is the
       part that is actually in the air, so it carries the contrast: foam at
       its foot and a wake spreading astern, which is what makes a fin read as
       a fin rather than as another piece of wreckage. */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 74'%3E%3Cg fill='%23031319' fill-opacity='.5'%3E%3Cpath d='M17 4c4.6 9.4 6.6 22.4 5.7 35.2C21.9 49.8 20 58 17 65c-3-7-4.9-15.2-5.7-25.8C10.4 26.4 12.4 13.4 17 4z'/%3E%3Cpath d='M12.4 31.5 1.5 42.5l10.4-3.4z'/%3E%3Cpath d='M21.6 31.5 32.5 42.5l-10.4-3.4z'/%3E%3Cpath d='M17 58.5 11 73l6-7 6 7z'/%3E%3C/g%3E%3Cg fill='none' stroke='%23e2f4f0' stroke-opacity='.26' stroke-width='1.7' stroke-linecap='round'%3E%3Cpath d='M17 41 8.5 56'/%3E%3Cpath d='M17 41 25.5 56'/%3E%3C/g%3E%3Cellipse cx='17' cy='41' rx='6.6' ry='2.5' fill='%23e2f4f0' fill-opacity='.42'/%3E%3Cpath d='M17 19 12.9 41h8.2z' fill='%230f2831'/%3E%3Cpath d='M17 19 15.3 41h1.7z' fill='%23355a65'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 34 74'%3E%3Cg transform='rotate(198 17 37)'%3E%3Cg fill='%23031319' fill-opacity='.45'%3E%3Cpath d='M17 4c4.6 9.4 6.6 22.4 5.7 35.2C21.9 49.8 20 58 17 65c-3-7-4.9-15.2-5.7-25.8C10.4 26.4 12.4 13.4 17 4z'/%3E%3Cpath d='M12.4 31.5 1.5 42.5l10.4-3.4z'/%3E%3Cpath d='M21.6 31.5 32.5 42.5l-10.4-3.4z'/%3E%3Cpath d='M17 58.5 11 73l6-7 6 7z'/%3E%3C/g%3E%3Cg fill='none' stroke='%23e2f4f0' stroke-opacity='.23' stroke-width='1.7' stroke-linecap='round'%3E%3Cpath d='M17 41 8.5 56'/%3E%3Cpath d='M17 41 25.5 56'/%3E%3C/g%3E%3Cellipse cx='17' cy='41' rx='6.6' ry='2.5' fill='%23e2f4f0' fill-opacity='.38'/%3E%3Cpath d='M17 19 12.9 41h8.2z' fill='%230f2831'/%3E%3Cpath d='M17 19 15.3 41h1.7z' fill='%23355a65'/%3E%3C/g%3E%3C/svg%3E");
  /* Riding the swell rather than travelling on it: a transform, so the whole
     field lifts on the compositor instead of repainting seven layers. */
  animation: flotsam 19s ease-in-out infinite;
}

@keyframes flotsam {
  0%, 100% { transform: translate(0, 0); }
  33% { transform: translate(4px, -2px); }
  66% { transform: translate(-3px, 2px); }
}

#track {
  position: relative;
  display: grid;
  /* minmax(0, 1fr) rather than 1fr — a bare 1fr floors at min-content, so a
     boat carrying more pips would widen its column and the lanes would not
     line up. */
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 0.3rem;
}

/* An empty stretch of water. The marker is a berth — the outline of the boat
   that would be lying there — rather than the box the lane is made of, so a
   square that fills reads as a boat pulling into a space its own shape.
 *
 * A fixed height, not a minimum: grid rows size to their tallest item, so a
 * boat that grew — a second line of name, a row of waiting crew — took its
 * whole row with it and shifted every other boat on the board. 5.35rem clears
 * the tallest a boat can be. */
.cell {
  position: relative;
  box-sizing: border-box;
  height: 5.35rem;
}

/* The berth is placed exactly where a hull would be: the same width a boat
   takes of its lane, and clear of the same strip at the head that a boat
   leaves for its wash. A boat coming in lands on its own outline.
 *
 * Drawn as SVG rather than a border, because a border follows the box and
 * this has to follow the boat. The polygon is `--plan` in a 0–100 viewBox and
 * has to be kept in step with it by hand — a percentage clip-path cannot be
 * handed to a background image. The box is inset by one unit all round so the
 * stroke is not sliced in half by the edge of the viewport. */
.cell::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0.3rem;
  bottom: 0;
  width: min(100%, 4.1rem);
  transform: translateX(-50%);
  background: center / 100% 100% no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1 -1 102 102' preserveAspectRatio='none'%3E%3Cpolygon points='50,0 58,3 68,10 78,22 86,38 91,56 93,74 93,93 90,100 10,100 7,93 7,74 9,56 14,38 22,22 32,10 42,3' fill='%23ffffff' fill-opacity='.035' stroke='%23e2f4f0' stroke-opacity='.26' stroke-width='1' stroke-dasharray='4 3' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");
}

/* -------------------------------------------------------------- islands --- */

/* The finish line: adjacent columns paying the same are merged into one
   island, so these are sand rather than water.
 *
 * The foot of one is a shore — the sand standing furthest into the water at
 * the middle and falling away to either side. Both bottom corners take half
 * the island's width, so the two quarter-ellipses meet on the centre line and
 * the whole edge is one curve however many columns the island spans. The head
 * is left square: the land carries on off the top of the chart.
 *
 * The dock sits on the centre line, which is the deepest sand there is, so
 * nothing had to be moved up the beach to make room for the curve. */
.island {
  position: relative;
  min-height: 4.75rem;
  border-radius: 0.35rem 0.35rem 50% 50% / 0.35rem 0.35rem 24% 24%;
  padding: 0.45rem;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 0.2rem;
  text-align: center;
  color: #4a3714;
  background:
    radial-gradient(70% 55% at 50% 15%, rgba(255, 245, 210, 0.7), transparent 70%),
    linear-gradient(180deg, var(--sand) 0%, var(--sand-2) 70%, #b08f4d 100%);
  border: 1px solid #8a6c2f;
  box-shadow:
    inset 0 1px 0 rgba(255, 250, 225, 0.7),
    /* Surf breaking along the shore. */
    0 0 0 2px rgba(226, 244, 240, 0.35),
    0 2px 6px rgba(0, 0, 0, 0.35);
}

/* A palm on the sand, drawn inline so the page still ships no image files. */
.island::before {
  content: "";
  position: absolute;
  top: 0.15rem; right: 0.3rem;
  width: 2rem; height: 2rem;
  opacity: 0.5;
  background: no-repeat center/contain
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg fill='%234a3714'%3E%3Cpath d='M19 38c0-8 1-14 3-20l2.4.7c-2.2 5.7-3.2 11.5-3.2 19.3z'/%3E%3Cpath d='M22 17c-4-4-9-5-13.5-3C12.5 10.4 18.6 11 22 17zM22 17c1-5.4 5.2-9.4 10.4-10.4C28 9.4 24.6 12.4 23.7 17.6zM22 17c4.2-3.2 10.4-3 14.4 1.2-5-2.2-10-1.2-13 1zM22 17c-2-5.2-6.2-8.2-11.4-8.2 5.2-.4 9.6 2.6 12.6 6.8z'/%3E%3C/g%3E%3C/svg%3E");
}

/* The two-point columns are worth spotting from across the board. */
.island.high {
  border-color: var(--brass-dark);
  box-shadow:
    inset 0 0 0 2px rgba(184, 137, 44, 0.7),
    inset 0 1px 0 rgba(255, 250, 225, 0.7),
    0 2px 8px rgba(0, 0, 0, 0.4);
}

.payout {
  display: flex; flex-direction: column; gap: 0.12rem;
  padding: 0.15rem 0.45rem;
  border-radius: 0.3rem;
  background: rgba(255, 248, 224, 0.5);
  border: 1px solid rgba(138, 108, 47, 0.5);
}
.pay {
  display: flex; align-items: center; gap: 0.3rem;
  font-size: 0.76rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.island.high .payout {
  border-color: var(--brass-dark);
  box-shadow: inset 0 0 0 1px rgba(227, 187, 99, 0.8);
}
.island.high .pay { font-size: 0.84rem; }

/* Drawn a little way up the beach. The sand falls away towards the corners,
   and an island holding one boat per column puts the outermost of them right
   where it does — measured at 1.7px into the water without this. The margin
   is on the dock rather than the island so it only costs height when there is
   something to hold. */
.docked {
  display: flex; gap: 0.3rem; flex-wrap: wrap;
  margin: 0.3rem 0 0.35rem;
}
/* Docked boats sit inside an island, not in a grid cell, so they keep their
   natural height rather than the row's. The beam is stated outright: the
   racing boats take it from the lane they are in, and a dock has no lane to
   measure — a percentage there collapses to the width of the seats.
 *
 * And it is a small beam. A landed boat is out of the race — nothing can be
 * voted onto it, moved out of it, or sunk with it — so the size it was
 * carrying is size the board can have back. What is left is what is still
 * worth reading: the colour it is painted, the crew it brought in, and the
 * number on its foredeck saying where it came in.
 *
 * Beached, too: no wash to leave room for, so the hull takes the whole box. */
.docked .boat {
  height: auto;
  width: 2.8rem;
  flex: none;
  margin-inline: 0;
  padding: 1.15rem 0.2rem 0.2rem;
}
.docked .hull { inset: 0; }

/* Two abreast still, at the size the smaller hull holds. */
.docked .pips { gap: 0.1rem; }
.docked .pips .pip { width: 0.56rem; height: 0.56rem; }
.docked .pips .pip-leak { font-size: 0.42rem; }

/* The plate is gone with the beam, but not the words on it: they are on the
   boat's own title now, and the plate stays in the page unseen so a reader
   that never sees the hull still gets the whole boat in one label.
 *
 * Written `.boat.landed` rather than `.landed`, because the rules that lay a
 * plate out — `.boat .name`, `.boat > .name` — are declared further down the
 * file at the same weight and would otherwise win on order alone, leaving the
 * plate in the flow at a couple of pixels tall. */
.boat.landed .name {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Where it came in, struck on the foredeck — brass, like everything on this
   page that is settled and cannot be argued with. It sits low enough on the
   deck that the bow has widened to hold it. */
.landed-mark {
  position: absolute;
  left: 50%; top: 0.32rem;
  z-index: 1;
  width: 0.78rem; height: 0.78rem;
  transform: translateX(-50%);
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--sans);
  font-size: 0.5rem; font-weight: 800;
  line-height: 1;
  color: #2b1e07;
  background: radial-gradient(circle at 35% 30%, var(--brass-lit), var(--brass) 70%, #8a6420);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 250, 225, 0.6);
}
/* A docked boat is beached. Motion stops bobbing it once it has arrived. */

/* --------------------------------------------------------------- boats ---- */

/* A boat is drawn in plan — the view from over the rail, looking straight
 * down at it. The bow points up the course, the six seats are amidships, and
 * the name is painted across the transom, which is where a boat carries it.
 *
 * The box is the boat's own water as well as the boat: the head of it is the
 * wash the bow throws, and the hull starts below that. */
.boat {
  position: relative;
  /* Matches .cell, so every row is the same height whatever is in it. */
  box-sizing: border-box;
  height: 5.35rem;
  /* Beam. A boat has to be longer than it is wide or it reads as an arch
     rather than a hull, and the lane is far wider than that allows — so the
     boat takes only as much of it as it needs and the sea shows either side.
     A definite width rather than a maximum: an auto margin on a grid item
     stops it stretching, which would leave every boat as wide as its own
     name plate and give the fleet a different beam each. */
  width: min(100%, 4.1rem);
  margin-inline: auto;
  /* Deep at the head: the wash, then the bow, which is too fine to seat
     anyone. Nothing is laid out there. */
  padding: 1.32rem 0.3rem 0.28rem;
  display: flex; flex-direction: column; gap: 0.15rem;
  color: #f6ecd6;
  /* The plan of the hull: a fine stem, the beam carried well aft, a square
     transom. Held in a custom property because the deck inside the hull is
     clipped to the same outline, which is what keeps the painted rail an even
     band the whole way round. */
  --plan: polygon(
    50% 0%, 58% 3%, 68% 10%, 78% 22%, 86% 38%, 91% 56%,
    93% 74%, 93% 93%, 90% 100%,
    10% 100%, 7% 93%, 7% 74%, 9% 56%, 14% 38%, 22% 22%, 32% 10%, 42% 3%);
}

/* The hull, on a layer of its own beneath the boat's contents.
 *
 * It has to be its own element rather than the boat's background, because the
 * clip-path that gives a boat its shape clips everything inside the element
 * that carries it — including crew floating off the stern. Here it shapes
 * only itself, and the boat above it is free to overhang.
 *
 * What shows of this layer is the gunwale: the painted rail running round the
 * sheer, in the boat's own colour, which is what names it from across the
 * board. */
.hull {
  position: absolute;
  /* Clear of the head of the box, where the wash goes. */
  inset: 0.3rem 0 0;
  z-index: 0;
  clip-path: var(--plan);
  background-color: var(--hull, #999);
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, 0.42), rgba(255, 255, 255, 0.06) 45%,
      rgba(0, 0, 0, 0.22));
  /* The boat's own shadow in the water. A drop-shadow follows the clipped
     silhouette, which a box-shadow — drawn round the rectangle the clip is
     cut from — cannot. */
  filter: drop-shadow(0 2px 2px rgba(4, 26, 32, 0.55));
}

/* The deck inside the rail: the same outline, inset, so the two stay
   parallel. Planked fore and aft, and in shadow — a boat's inside is the
   one part of it the sun does not reach, and dark boards give the crew
   discs and the name plate something to read against on every hull. */
.hull::before {
  content: "";
  position: absolute;
  inset: 4px;
  clip-path: var(--plan);
  background-color: var(--hull, #999);
  background-image:
    radial-gradient(78% 62% at 50% 42%, rgba(0, 0, 0, 0.52), rgba(0, 0, 0, 0.76)),
    repeating-linear-gradient(90deg,
      rgba(255, 255, 255, 0.05) 0 1px, rgba(0, 0, 0, 0.16) 1px 2px, transparent 2px 7px);
}

/* The foredeck. A boat's bow is decked over, which is also the reason nobody
   is seated up there. Clipped twice: by its own wedge, which gives the deck
   its aft edge, and by the hull's outline, which is what makes the wedge a
   bow. */
.hull::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 33%;
  clip-path: polygon(0 0, 100% 0, 100% 58%, 50% 100%, 0 58%);
  background-color: var(--hull, #999);
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, 0.3), rgba(0, 0, 0, 0.12) 60%,
      rgba(0, 0, 0, 0.3)),
    repeating-linear-gradient(90deg,
      rgba(0, 0, 0, 0.12) 0 1px, transparent 1px 6px);
}

/* The wash off the bow. It lives in the padding at the head of the box rather
   than outside it, so a boat never draws over the island or the boat ahead. */
.wake {
  position: absolute;
  left: 50%; top: 0.1rem;
  width: 2.2rem; height: 0.72rem;
  transform: translateX(-50%);
  z-index: 0;
  pointer-events: none;
  /* The crest the bow pushes ahead of it: an arc struck from a centre below
     the stem, so it curves away on both bows the way a bow wave does. Kept
     faint — it is water, and it sits where the eye looks first. */
  background: radial-gradient(120% 108% at 50% 130%,
    rgba(255, 255, 255, 0) 52%,
    rgba(255, 255, 255, 0.42) 68%,
    rgba(226, 244, 240, 0.18) 78%,
    rgba(226, 244, 240, 0) 96%);
}
/* A boat that has arrived, or gone under, has stopped making way. */
.docked .wake, .boat.sunk .wake { display: none; }

/* The two things laid out in the boat ride above the hull. Named rather than
   excluded, so anything else a boat grows — the crew adrift, the wash — is
   free to place itself without being dragged back into the flow. */
.boat > .pips, .boat > .name { position: relative; z-index: 1; }

/* Crew put over the side and not yet placed: pips floating astern on a short
   tether. Hanging below the hull is safe — boats are dealt distinct columns
   at setup and only ever change row, so the cell under a boat is open water
   for the whole game. */
.adrift {
  position: absolute;
  left: 50%;
  /* Clear of the transom, whose plate now sits at the foot of the boat, and
     no lower than the water the sea's own foot leaves for it. */
  bottom: -0.95rem;
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  gap: 0.12rem;
  padding: 0.08rem 0.18rem;
  border-radius: 1rem;
  background: rgba(6, 34, 40, 0.72);
  box-shadow:
    0 0 0 1px rgba(226, 244, 240, 0.35),
    0 2px 4px rgba(0, 0, 0, 0.5);
}
/* Smaller than the crew still aboard: they are over the side, not in a seat. */
.adrift .pip { width: 0.7rem; height: 0.7rem; }
.adrift .pip-captain { margin: 1px; }
/* The rope back to the transom it left. */
.adrift::before {
  content: "";
  position: absolute;
  left: 50%;
  top: -0.32rem;
  width: 0;
  height: 0.32rem;
  border-left: 2px dashed rgba(226, 244, 240, 0.7);
}

/* The swell, the slide between squares and the roll of a sinking boat are all
   driven by Motion from animate.js rather than declared here: they have to
   survive the board being rebuilt on every server frame, which a CSS
   animation cannot do — it restarts from zero each time its element is
   replaced. `transform` on a boat therefore belongs to Motion alone, and
   nothing in this file may set it. */

/* The name painted across the transom. It carries the boat's colour and
   nothing else: the count and the leaks it used to spell out are the six
   seats above it, said better. The full state is on the plate's title for
   anyone reading the page rather than looking at it. */
.boat .name {
  align-self: center;
  /* Inside the rail at the height it sits at, so the plate is painted on the
     transom rather than nailed across it. */
  max-width: 76%;
  font-size: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 0.09rem 0.24rem;
  border-radius: 0.12rem;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.45);
  color: #fdf3dc;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.7);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
}

/* A sunk boat and its crew are out of the game: drained of colour, listing,
   and marked. */
.boat.sunk {
  filter: grayscale(1) brightness(0.65);
  opacity: 0.55;
  /* The resting pose of a wreck. Motion animates the roll into it and then
     hands `transform` back, so this is what holds it from then on. */
  transform: rotate(-7deg);
}
.boat.sunk .name { text-decoration: line-through; }
.boat.sunk .pip { filter: grayscale(1); }
.boat.sunk::after {
  content: "";
  position: absolute;
  inset: 0;
  background: no-repeat center/1.6rem
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23f2e5c8' d='M12 2C7 2 3.5 5.2 3.5 9.4c0 2.6 1.3 4.4 2.7 5.5.5.4.8.9.8 1.5V18c0 .6.4 1 1 1h1.4v-2h1.5v2h2.2v-2h1.5v2H16c.6 0 1-.4 1-1v-1.6c0-.6.3-1.1.8-1.5 1.4-1.1 2.7-2.9 2.7-5.5C20.5 5.2 17 2 12 2z'/%3E%3Ccircle cx='8.6' cy='9.9' r='2.1' fill='%23241a10'/%3E%3Ccircle cx='15.4' cy='9.9' r='2.1' fill='%23241a10'/%3E%3Cpath d='M12 12.8l1.3 2.2h-2.6z' fill='%23241a10'/%3E%3C/svg%3E");
  pointer-events: none;
}

/* A leak or a sinking, the moment it happens. The wash lives on a pseudo-
   element rather than the hull so it runs at once, independent of the bob
   delay app.js sets inline on the boat itself. */
.boat.flash-leak::before,
.boat.flash-sink::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  animation: wash 1.3s ease-out forwards;
}
.boat.flash-leak::before {
  background: radial-gradient(circle at 50% 75%,
    rgba(53, 198, 192, 0.95), rgba(53, 198, 192, 0) 72%);
}
.boat.flash-sink::before {
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 255, 255, 0.95), rgba(14, 63, 73, 0) 78%);
  animation-duration: 1.6s;
}
@keyframes wash {
  0% { opacity: 0; transform: scale(0.6); }
  22% { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.15); }
}

/* A boat someone has already taken from is closed for the rest of the phase.
   The mark has to follow the hull rather than box it in: an outline would
   draw the rectangle the boat is laid out in, not the boat. Four offset
   drop-shadows of the hull's own silhouette make a rope round the sheer. */
.boat.locked .hull {
  filter:
    drop-shadow(0 2px 2px rgba(4, 26, 32, 0.55))
    drop-shadow(1.5px 0 0 rgba(255, 244, 214, 0.8))
    drop-shadow(-1.5px 0 0 rgba(255, 244, 214, 0.8))
    drop-shadow(0 1.5px 0 rgba(255, 244, 214, 0.8))
    drop-shadow(0 -1.5px 0 rgba(255, 244, 214, 0.8));
}
.boat.locked .name { opacity: 0.7; }

/* ---------------------------------------------------------------- pips ---- */

/* The seats, amidships: three thwarts of two, port and starboard of the keel
   line. The arrangement is fixed rather than wrapped — a boat always shows
   six seats, and letting them reflow would put a row of them across the bow,
   where there is no beam to hold them. */
.pips {
  display: grid;
  grid-template-columns: repeat(2, auto);
  justify-content: center; align-content: center;
  gap: 0.14rem;
  flex: 1;
}
/* A shade smaller than a pip elsewhere on the page: two abreast and three
   deep has to fit inside a hull that is only as wide as a hull should be. */
.pips .pip { width: 0.69rem; height: 0.69rem; }
.pips .pip-leak { font-size: 0.5rem; }

/* Every crew token names itself on hover — a cap and a bare head are a few
   pixels apart at this size, and resting on one should settle it. The cursor
   is the only sign a tooltip is there to be waited for. */
.pips .pip, .adrift .pip { cursor: help; }

/* Crew are discs set into the deck, each rimmed in dark rope. */
.pip {
  position: relative;
  width: 0.85rem; height: 0.85rem;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.55);
  display: inline-block;
  flex: none;
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.45),
    inset 0 -1px 2px rgba(0, 0, 0, 0.3);
  /* Default fill for legend and payout pips, which carry no owner colour. */
  background: color-mix(in srgb, var(--ink) 45%, transparent);
}

/* A captain is the one in the cap, and the boats are drawn from above, so
   that is what you see of one: the crown of the cap in the player's colour,
   the brass band round it, and the peak standing out forward.
 *
 * The band is struck inside the disc rather than round it — worn outside it
 * needed a margin to keep clear of its neighbours, and three pixels on every
 * seat is a wider boat. */
.pip-captain {
  box-shadow:
    inset 0 0 0 2px var(--brass),
    inset 0 0 0 3px rgba(0, 0, 0, 0.35),
    inset 0 1px 1px rgba(255, 255, 255, 0.4);
}

/* The peak, over the bow — everyone aboard is facing the way the boat is
   going. It hangs off the disc rather than sitting inside it, so a cap costs
   a captain no more room than a bare head: the six seats have to hold
   whoever is in them. Sized in per cent of the disc, since a pip is smaller
   aboard a boat than it is in the legend, and the cap has to fit both. */
.pip-captain::before {
  content: "";
  position: absolute;
  left: 50%;
  /* Wide and shallow, the way a peak is, and mostly clear of the crown: the
     colour under the cap is whose captain it is, and a peak that shaded it
     would leave three captains looking alike. */
  top: -21%;
  width: 94%;
  height: 38%;
  transform: translateX(-50%);
  /* Round over the front, straight along the band. */
  border-radius: 52% 52% 22% 22% / 88% 88% 18% 18%;
  background: linear-gradient(180deg, #342d24 0%, #16120d 75%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.24),
    0 0 0 1px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}
.pip-pirate { border-width: 1px; }

/* A leak: a teal disc carrying a capital L, so it reads as a leak rather than
   as somebody's token whatever colour boat it sits on. Declared after .pip so
   it wins the background. */
.pip-leak {
  background: radial-gradient(circle at 35% 30%, #7ce5df, var(--leak) 60%, #16918c);
  border: 1px solid var(--leak-edge);
  color: var(--leak-ink);
  font-size: 0.6rem;
  font-weight: 800;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Water coming in: a ring spreading out from the breach, over and over. */
.pip-leak::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  border: 1px solid var(--leak);
  animation: ripple 2.2s ease-out infinite;
  pointer-events: none;
}
@keyframes ripple {
  0% { transform: scale(1); opacity: 0.9; }
  100% { transform: scale(2.4); opacity: 0; }
}

/* An unoccupied seat. Declared after .pip so it wins the background. */
.pip-empty {
  background: transparent;
  border: 1px dashed rgba(0, 0, 0, 0.45);
  box-shadow: none;
  opacity: 0.6;
}
/* Every deck is in shadow now, whatever colour the boat is painted, so an
   empty seat is scribed in light on all of them rather than only the dark
   hulls. The dark rope rim above still belongs to the legend and the payouts,
   which sit on parchment. */
.boat .pip-empty { border-color: rgba(255, 255, 255, 0.5); }


/* ------------------------------------------------------- hand and cards ---- */

#hand {
  margin-top: 0.7rem;
  padding: 0.7rem 0.85rem;
  font-size: 0.9rem;
  border-radius: 0.35rem;
  background: linear-gradient(160deg, rgba(255, 251, 238, 0.6), rgba(218, 199, 162, 0.5));
  border: 1px solid rgba(121, 92, 51, 0.45);
  box-shadow: inset 0 0 0 3px rgba(255, 250, 235, 0.35);
}
#hand:empty { display: none; }
#hand > strong {
  display: block;
  font-family: var(--display);
  font-size: 1.02rem;
  letter-spacing: 0.02em;
  margin-bottom: 0.5rem;
  color: var(--ink);
}

.cards-row { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.45rem; }

/* A playing card: small, thick-edged, and clearly a thing you hold. Bare
   `.card`, because these also appear on their own in the committed panel — the
   home panels outrank this with `.cards > .card`. */
.card {
  padding: 0.32rem 0.7rem;
  border-radius: 0.25rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: capitalize;
  border: 1px solid rgba(0, 0, 0, 0.45);
  color: #1a1408;
  background-color: var(--hull, transparent);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(0, 0, 0, 0.18));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.45),
    0 1px 2px rgba(60, 42, 18, 0.3);
}
.card.dark { color: #f6ecd6; }
button.card { cursor: pointer; }
/* A card keeps its hull on hover — the colour is what the card means, and the
   text is picked to read against it. So the feedback is a brass rim and a
   lift, which show on any hull without touching the contrast underneath. */
button.card:hover:not(:disabled) {
  transform: translateY(-1px);
  border-color: var(--brass-dark);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    0 0 0 2px var(--brass-lit),
    0 2px 5px rgba(40, 26, 8, 0.45);
}
button.card:focus-visible { outline-offset: 3px; }
button.card:disabled { cursor: default; opacity: 0.45; }

/* A boat already out of the game: its card is dead for the rest of the race. */
.card.spent { filter: grayscale(1); text-decoration: line-through; }

/* The captain card carries no boat colour of its own — brass, like the rank. */
.card-captain {
  background-color: transparent;
  background-image: linear-gradient(180deg, var(--brass-lit), var(--brass));
  border: 1px solid var(--brass-dark);
  color: #2a1c05;
}

/* A captain names a boat rather than being one, so mark it apart from a
   pirate card of the same colour. */
.card-captain-target { box-shadow: inset 0 0 0 2px var(--brass); }

/* The captain's "no leak at all": it names no boat, so it carries no boat
   colour either — struck through to read as a refusal rather than a choice. */
.card-none, .vote-none {
  background-color: rgba(51, 38, 26, 0.12);
  background-image: none;
  border: 1px dashed rgba(51, 38, 26, 0.5);
  color: var(--ink);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  box-shadow: none;
}
.card-none.card-captain-target { box-shadow: none; }

.chooser {
  margin-top: 0.7rem; padding: 0.65rem 0.75rem;
  border-radius: 0.3rem;
  border: 1px solid var(--brass-dark);
  background: rgba(255, 249, 232, 0.6);
  box-shadow: inset 0 0 0 1px rgba(227, 187, 99, 0.5);
  font-weight: 600;
}

/* The one card you may see before the reveal — your own. */
.committed {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  margin-top: 0.5rem; padding: 0.55rem 0.75rem;
  border-radius: 0.3rem;
  border: 1px dashed var(--rope-dark);
  background: rgba(120, 92, 52, 0.12);
  font-size: 0.85rem;
  font-style: italic;
}
.committed .card { font-style: normal; }

/* -------------------------------------------------------------- reveal ---- */

#reveal { margin: 0 0 1.2rem; font-size: 0.9rem; }
#reveal > strong {
  display: block;
  font-family: var(--display);
  font-size: 1rem;
  letter-spacing: 0.02em;
  margin: 0.9rem 0 0.1rem;
  padding-bottom: 0.2rem;
  border-bottom: 1px solid rgba(121, 92, 51, 0.4);
}
#reveal > strong:first-child { margin-top: 0; }

.vote-chip {
  padding: 0.28rem 0.55rem; border-radius: 0.25rem;
  font-size: 0.75rem; font-weight: 700;
  border: 1px solid rgba(0, 0, 0, 0.45);
  color: #1a1408;
  background-color: var(--hull, transparent);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.25), rgba(0, 0, 0, 0.18));
}
.vote-chip.dark { color: #f6ecd6; }

/* The card that carried the vote. Everyone who backed the winning boat is
   ringed, not just one chip — a vote is a count, and the ring is what makes
   the count readable without adding up the row. Brass, since that is what
   every settled thing on this page is edged in. */
.vote-chip.won {
  /* Dark either side of the brass: inside, so the ring separates from a hull
     it shares a hue with — a brass ring straight onto the yellow chip all but
     disappeared — and outside, so it separates from the parchment too. */
  border-color: rgba(0, 0, 0, 0.75);
  box-shadow:
    0 0 0 2px var(--brass-lit),
    0 0 0 3px rgba(40, 26, 8, 0.8),
    0 1px 4px rgba(40, 26, 8, 0.4);
}

.outcome {
  margin: 0.5rem 0 0;
  /* Light enough that the tie-break clause inside it has somewhere to go: at
     600 the display face lands on its bold weight already, and a `strong`
     nested in it would have come out identical. */
  font-weight: 400;
  font-family: var(--display);
  font-size: 0.95rem;
  color: var(--ink);
  padding-left: 0.7rem;
  border-left: 3px solid var(--rope);
}

/* A colour named in an outcome is always a boat or a player, so it is worn
   rather than spelled — the same chip the ballot above it uses, cut down to
   sit inside a sentence. */
.ref {
  padding: 0 0.28rem;
  border-radius: 0.2rem;
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 700;
  color: #1a1408;
  background-color: var(--hull, transparent);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.25), rgba(0, 0, 0, 0.18));
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.45);
}
.ref.dark { color: #f6ecd6; }

/* The starting player settling a tie is the one result the table did not
   reach on its own. */
.outcome .tiebreak { font-weight: 700; color: var(--brass-dark); }

.swatch {
  width: 0.95rem; height: 0.95rem; border-radius: 0.2rem;
  border: 1px solid rgba(0, 0, 0, 0.5);
  background-color: var(--hull, transparent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4);
}

/* ------------------------------------------------------------ ship's log --- */

.logbook-label {
  margin: 0 0 0.35rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brass-dark);
}
#gameover h2 { margin: 0 0 0.3rem; font-size: 1.5rem; }
#gameover .row { margin-top: 0.9rem; }
#standings { margin: 0.85rem 0 0; padding: 0; list-style: none; }

.standing {
  padding: 0.6rem 0.75rem; border-radius: 0.3rem;
  border: 1px solid rgba(121, 92, 51, 0.35);
  background: rgba(255, 250, 236, 0.4);
}
.standing + .standing { margin-top: 0.4rem; }
/* The winner is worth spotting without reading the numbers. */
.standing.winner {
  border-color: var(--brass-dark);
  background: linear-gradient(180deg, rgba(255, 244, 214, 0.9), rgba(238, 214, 158, 0.7));
  box-shadow: inset 0 0 0 1px rgba(227, 187, 99, 0.8);
}
.standing-head { display: flex; align-items: center; gap: 0.5rem; }
.standing-name { font-weight: 700; }
.standing-total {
  margin-left: auto;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  color: var(--brass-dark);
}
.standing-detail { margin-top: 0.3rem; font-size: 0.82rem; color: var(--ink-2); }
.standing-detail.muted { color: var(--ink-3); font-style: italic; }

/* --------------------------------------------------- boat and crew hues ---- */

/* `--hull` carries the colour so a single custom property can drive a hull's
   background layers, a swatch, a chip and a card without each needing its own
   colour rule. */
.c-red { --hull: var(--red); }
.c-orange { --hull: var(--orange); }
.c-yellow { --hull: var(--yellow); }
.c-green { --hull: var(--green); }
.c-blue { --hull: var(--blue); }
.c-white { --hull: var(--white); }
.c-purple { --hull: var(--purple); }
.c-black { --hull: var(--black); }

/* A pip is a flat disc, not a planked hull, so it takes the colour directly. */
.pip.c-red { background: var(--red); }
.pip.c-orange { background: var(--orange); }
.pip.c-yellow { background: var(--yellow); }
.pip.c-green { background: var(--green); }
.pip.c-blue { background: var(--blue); }
.pip.c-white { background: var(--white); }
.pip.c-purple { background: var(--purple); }
.pip.c-black { background: var(--black); }

/* A boat needs no light-on-dark rule of its own any more: every deck is in
   shadow now, whatever the hull is painted, so `.boat` sets the one text
   colour. The class is still on the element, since the same eight colours
   drive the cards and chips, which do still split light from dark. */

/* ----------------------------------------------------------- colophon ---- */

/* Where a chart carries its printer's mark: the foot of the sheet, hard right,
   quiet enough to be looked past and legible enough to be read when wanted. */
.colophon {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: baseline;
  gap: 0.5rem;
  margin-top: 1.4rem;
  padding-top: 0.6rem;
  border-top: 1px solid rgba(121, 92, 51, 0.3);
  font-size: 0.74rem;
  /* --ink-2 rather than --ink-3: small print still has to clear 4.5:1 on
     parchment, and --ink-3 comes to 3.7:1 at this size. */
  color: var(--ink-2);
}

.colophon .version {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--brass-dark);
}

.colophon a {
  color: var(--brass-dark);
  text-decoration-color: rgba(109, 79, 20, 0.45);
  text-underline-offset: 0.15em;
}
.colophon a:hover { text-decoration-color: currentColor; }

/* -------------------------------------------------------------- narrow ---- */

/* Too narrow to run the board and the rail side by side: the log folds back
   under the board, keeping its own scroll so it still cannot push the board
   off the screen, and closed by default so it costs one line until asked for. */
@media (max-width: 62rem) {
  /* The height becomes a floor rather than a fixed measure: lanes get narrow
     enough here that a boat can want a pixel or two more, and a fixed height
     would clip it rather than let the row grow. */
  .cell, .boat { height: auto; min-height: 5.35rem; }
  .docked .boat { min-height: 0; }

  .play { flex-direction: column; }
  #log.rail {
    flex: none;
    width: 100%;
    position: static;
    max-height: 18rem;
  }
}

/* On a phone the chart is nearly the whole screen, so the parchment gives up
   its margins. The track itself keeps its minimum width and scrolls. */
@media (max-width: 40rem) {
  main {
    margin: 0;
    padding: 1.1rem 0.9rem 1.5rem;
    border-radius: 0;
    border-left: none;
    border-right: none;
  }
  .masthead { gap: 0.7rem; }
  .compass { width: 2.6rem; height: 2.6rem; }
  h1 { font-size: 1.7rem; }
  #status { padding: 0.5rem 1.2rem; }
}

/* ------------------------------------------------------------- motion ----- */

/* Everything decorative stops; nothing that carries meaning depends on it. */
@media (prefers-reduced-motion: reduce) {
  #sea::before, #sea::after, .pip-leak::after,
  .boat.flash-leak::before, .boat.flash-sink::before {
    animation: none !important;
  }
  /* The wash would otherwise sit over the hull at full opacity for good. */
  .boat.flash-leak::before, .boat.flash-sink::before { display: none; }
  button.card:hover:not(:disabled),
  button:active:not(:disabled) { transform: none; }
}
