:root {
  color-scheme: light dark;
  --bg: #0a1830;
  --fg: #f4f8fc;
  --accent: #1ca8dd;
  --muted: #9db4cc;
  --error: #ff6b6b;
  --surface: #0d2745;
}

* {
  box-sizing: border-box;
}

[hidden] {
  display: none !important;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, var(--bg), var(--surface));
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--fg);
}

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem 1rem;
  padding: 0.75rem 1.5rem;
  border-bottom: 1px solid rgba(157, 180, 204, 0.2);
}

.nav-links,
.topbar-right {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

.nav-brand {
  color: var(--fg);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  margin-right: 0.75rem;
}

.nav-link {
  color: var(--muted);
  text-decoration: none;
  font-weight: 600;
  padding: 0.35rem 0.1rem;
  border-bottom: 2px solid transparent;
}

.nav-link:hover {
  color: var(--fg);
}

.nav-link.active {
  color: var(--fg);
  border-bottom-color: var(--accent);
}

.topbar-right {
  margin-left: auto;
}

.topbar .btn {
  padding: 0.3rem 0.8rem;
  font-size: 0.8rem;
  border-radius: 5px;
}

.topbar .nav-brand {
  font-size: 0.9rem;
}

.topbar .nav-link {
  font-size: 0.85rem;
}

.topbar .user-status {
  font-size: 0.8rem;
}

.content-main {
  flex: 1;
  width: 100%;
  max-width: 44rem;
  margin: 0 auto;
  padding: 2.5rem 1.5rem 4rem;
  justify-content: flex-start;
  text-align: left;
}

.content-center {
  justify-content: center;
  text-align: center;
}

.prose {
  width: 100%;
  color: var(--fg);
  line-height: 1.65;
}

.prose h2 {
  font-size: 1.15rem;
  margin: 1.75rem 0 0.5rem;
}

.prose p,
.prose li {
  color: var(--muted);
}

.prose strong {
  color: var(--fg);
}

.prose-note {
  border-left: 3px solid var(--accent);
  padding-left: 1rem;
  margin-top: 2rem;
}

.prose-back {
  margin-top: 2rem;
}

.prose-back a {
  color: var(--accent);
}

.prose-sponsor {
  margin-top: 1rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.prose-sponsor a {
  color: var(--accent);
}

.user-status {
  color: var(--muted);
  font-size: 0.9rem;
}

main {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2.5rem;
  padding: 0 1rem 4rem;
  text-align: center;
}

h1 {
  color: var(--fg);
  font-size: clamp(2rem, 6vw, 4.5rem);
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0 1rem 0.5rem;
  margin: 0;
  text-shadow: 0 0 24px rgba(28, 168, 221, 0.5);
  border-bottom: 4px solid var(--accent);
}

.status-text {
  color: var(--fg);
  font-size: 0.9rem;
  min-height: 1.2em;
  margin: 0;
  max-width: 32rem;
}

.btn {
  display: inline-block;
  border: none;
  border-radius: 6px;
  padding: 0.7rem 1.5rem;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.1s ease, opacity 0.1s ease;
}

.btn:hover {
  opacity: 0.9;
}

.btn:active {
  transform: scale(0.98);
}

.btn-primary {
  background: var(--accent);
  color: #04121f;
}

.btn-outline {
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--muted);
}

.modal {
  position: fixed;
  inset: 0;
  background: rgba(4, 12, 24, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 10;
}

.modal[hidden] {
  display: none;
}

.modal-content {
  position: relative;
  background: var(--surface);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 10px;
  padding: 2rem;
  width: 100%;
  max-width: 22rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.modal-content h2 {
  margin: 0;
  color: var(--fg);
}

.modal-close {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.modal-content form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.modal-content label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  color: var(--muted);
  font-size: 0.85rem;
  text-align: left;
}

.modal-content input {
  padding: 0.6rem 0.7rem;
  border-radius: 6px;
  border: 1px solid rgba(157, 180, 204, 0.3);
  background: rgba(255, 255, 255, 0.05);
  color: var(--fg);
  font-size: 1rem;
}

.modal-switch {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0;
  text-align: center;
}

.modal-switch a {
  color: var(--accent);
}

.form-error {
  color: var(--error);
  font-size: 0.85rem;
  min-height: 1.1em;
  margin: 0;
}

.btn-small {
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
}

.banner {
  display: flex;
  align-items: center;
  gap: 1rem;
  background: rgba(28, 168, 221, 0.12);
  border: 1px solid rgba(28, 168, 221, 0.4);
  border-radius: 8px;
  padding: 0.75rem 1.25rem;
  color: var(--fg);
  font-size: 0.9rem;
}

.admin-main {
  display: block;
  text-align: left;
  flex: 1;
  padding: 2rem 1.5rem 4rem;
  max-width: 60rem;
  margin: 0 auto;
  width: 100%;
}

.admin-title {
  display: inline-block;
  margin: 0 0 1.5rem;
  font-size: 2rem;
}

.admin-section {
  margin-bottom: 2.5rem;
}

.admin-section h2 {
  font-size: 1.2rem;
  margin-bottom: 0.75rem;
}

.stat-row {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.stat-tile {
  flex: 1;
  min-width: 8.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
  background: var(--surface);
}

.stat-value {
  font-size: 1.8rem;
  font-weight: 700;
  color: var(--accent);
}

.stat-label {
  color: var(--muted);
  font-size: 0.85rem;
  margin-top: 0.25rem;
}

.table-wrap {
  overflow-x: auto;
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
}

.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.admin-table th,
.admin-table td {
  text-align: left;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid rgba(157, 180, 204, 0.15);
  white-space: nowrap;
}

.admin-table th {
  color: var(--muted);
  font-weight: 600;
}

.admin-table tbody tr:last-child td {
  border-bottom: none;
}

/* The signed-in player's own row on a leaderboard (see reports.js). */
.admin-table tr.is-me td {
  background: rgba(79, 168, 224, 0.14);
  font-weight: 600;
}

/* ---- Insights page (insights.html / insights.js) ---------------------------------------------------------------- */
.insights-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  align-items: center;
  margin-bottom: 0.75rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.insights-controls select {
  margin-left: 0.4rem;
  padding: 0.35rem 0.5rem;
  border-radius: 6px;
  border: 1px solid var(--muted);
  background: var(--surface);
  color: var(--fg);
}

.insights-check {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.insights-note,
.insights-help {
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.45;
}

.insights-note {
  margin: 0 0 2rem;
}

.insights-help {
  margin: 0 0 0.75rem;
}

.insights-empty {
  color: var(--muted);
  font-size: 0.9rem;
  font-style: italic;
  margin: 0.25rem 0;
}

.admin-section h3 {
  margin: 1.5rem 0 0.6rem;
  font-size: 1rem;
  color: var(--fg);
}

.admin-section h3 .insights-help {
  margin: 0;
  font-weight: 400;
}

.admin-section h4 {
  margin: 1.1rem 0 0.4rem;
  font-size: 0.9rem;
  color: var(--muted);
}

.stat-note {
  color: var(--muted);
  font-size: 0.75rem;
  margin-top: 0.15rem;
}

.stat-row {
  margin-bottom: 0.5rem;
}

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

.bar-row {
  display: grid;
  grid-template-columns: minmax(7rem, 14rem) 1fr 4rem;
  gap: 0.75rem;
  align-items: center;
  margin-bottom: 0.45rem;
  font-size: 0.9rem;
}

.bar-label {
  color: var(--fg);
  line-height: 1.25;
}

.bar-sub {
  display: block;
  color: var(--muted);
  font-size: 0.75rem;
}

.bar-track {
  height: 0.7rem;
  border-radius: 999px;
  background: rgba(157, 180, 204, 0.15);
  overflow: hidden;
}

.bar-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
}

.bar-value {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
  font-weight: 700;
}

.chart-box {
  padding: 0.5rem 0.75rem 0.4rem;
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
  background: var(--surface);
}

.chart-svg {
  width: 100%;
  height: auto;
  display: block;
}

.chart-axis {
  stroke: rgba(157, 180, 204, 0.35);
  stroke-width: 1;
}

.chart-label {
  fill: var(--muted);
  font-size: 9px;
}

.chart-bar-views {
  fill: rgba(157, 180, 204, 0.55);
}

.chart-bar-starts {
  fill: var(--accent);
}

.chart-legend {
  display: flex;
  gap: 1rem;
  margin-top: 0.25rem;
  color: var(--muted);
  font-size: 0.75rem;
}

.chart-legend span::before {
  content: "";
  display: inline-block;
  width: 0.7rem;
  height: 0.7rem;
  margin-right: 0.35rem;
  border-radius: 2px;
  vertical-align: -1px;
}

.legend-views::before {
  background: rgba(157, 180, 204, 0.55);
}

.legend-starts::before {
  background: var(--accent);
}

@media (max-width: 760px) {
  .bar-row {
    grid-template-columns: minmax(6rem, 9rem) 1fr 3rem;
    gap: 0.5rem;
  }
}

.form-message {
  color: var(--muted);
  font-size: 0.85rem;
  min-height: 1.1em;
  margin: 0;
}

.you-label {
  color: var(--muted);
  font-size: 0.85rem;
}

.game-page {
  height: 100vh;
  height: 100dvh;
  min-height: 0;
}

.game-main {
  flex: 1;
  min-height: 0;
  justify-content: flex-start;
  align-items: stretch;
  gap: 0.6rem;
  padding: 0.75rem 1rem 0.9rem;
}

.race {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 0.75rem;
}

.plot {
  position: relative;
  flex: 1;
  min-width: 0;
  min-height: 0;
  border: 1px solid rgba(157, 180, 204, 0.25);
  border-radius: 10px;
  overflow: hidden;
  background: linear-gradient(180deg, #11406b, #0a2a4c);
  perspective: 950px;
  perspective-origin: 50% 4%;
}

/* Tilts the course back like a road receding toward the mark, so the boat's direction of travel (always
   toward the top of this view - see WorldTransform's flip for the downwind event) reads as "forward". */
.plot svg {
  display: block;
  width: 100%;
  height: 100%;
  transform: rotateX(37deg);
  transform-origin: 50% 100%;
}

/* The sailor's-eye view: a canvas filling the plot, in front of the top-down mini-map and end-of-run view
   that share this same container (see .minimap-box / .end-course below). */
.cockpit-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* The sailor's-eye page's wind dial and wind history, stacked in one left-hand column (see
   sailor-main.js's leftHud), overlaid directly on the forward-looking view (see .cockpit-canvas) rather than
   standing beside it, and shown only while a run is on the water (see sailor-main.js's currentPhase()), never
   on the pre-race setup screen. The course mini-map and the Ghost Boat card sit opposite, in the right-hand
   column (see .ghost-overlay below). */
.left-hud {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 4;
  width: 11rem;
  max-width: calc(50% - 1.1rem);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* The corner mini-map leans back much less than the plots in the setup screens: the full 37 degrees squeezes
   the top of the course toward the bottom of the panel, pushing the weather mark about a fifth of the way down. */
.minimap-overlay svg {
  transform: rotateX(14deg);
}

.minimap-overlay {
  width: 100%;
  /* The mini-map shows about 1040 x 1180 m of the course (see view.js's MINI_* limits) - matching this panel's
     own proportions to that, rather than forcing it into a square, is what lets the course fill the panel
     edge to edge instead of leaving large letterboxed gaps top and bottom. */
  aspect-ratio: 9 / 10;
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
}

/* Heads the left-hand column, same width and card treatment as the panels below it - a boat-relative
   compass (see sailor-main.js's frame()) with a small hull silhouette, bow always pointing up (the boat's own heading), and two arrows running from the ring's edge to
   the hull, each pointing at the boat: "T" for the true wind, "A" for the apparent wind, both relative to the
   boat right now. */
.wind-dir-overlay {
  width: 100%;
  aspect-ratio: 1;
  padding: 0.5rem;
  background: rgba(4, 12, 24, 0.55);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
}

.wind-dir-overlay svg {
  width: 100%;
  height: 100%;
  display: block;
}

.wind-dir-ring {
  fill: none;
  stroke: rgba(157, 180, 204, 0.35);
  stroke-width: 1.5;
}

.wind-dir-hull {
  fill: rgba(244, 248, 252, 0.85);
  stroke: rgba(157, 180, 204, 0.6);
  stroke-width: 1;
}

.wind-dir-line {
  stroke-width: 5;
  stroke-linecap: round;
}

.wind-dir-line.true-wind {
  stroke: #7fe8ff;
}

.wind-dir-line.apparent-wind {
  stroke: #ff9f1c;
}

.wind-dir-arrow.true-wind {
  fill: #7fe8ff;
}

.wind-dir-arrow.apparent-wind {
  fill: #ff9f1c;
}

.wind-dir-label {
  font-size: 16px;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
}

.wind-dir-label.true-wind-label {
  fill: #7fe8ff;
}

.wind-dir-label.apparent-wind-label {
  fill: #ff9f1c;
}

/* The downwind leg's masthead fly (see sailor-main.js's updateFly()): the same boat-relative ring as the wind dial
   above, with the apparent-wind arrow and, on the ring, a marker at the apparent wind angle that gives the best
   VMG - green when the arrow is on it, amber a little off, red well off. */
.fly-panel {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  padding: 0.4rem 0.5rem 0.5rem;
  background: rgba(4, 12, 24, 0.55);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
}

.fly-panel svg {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  /* The sailor's-eye plot tilts every svg inside it (see .plot svg); the dial is a flat top-down ring. */
  transform: none;
}

.fly-label {
  color: var(--muted);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.fly-target {
  fill: none;
  stroke-width: 14;
  stroke-linecap: round;
  opacity: 0.9;
  stroke: #39ff6a;
}

.fly-target.near {
  stroke: #ffb020;
}

.fly-target.off {
  stroke: #ff4d4d;
}

.wind-overlay {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.65rem 0.8rem;
  background: rgba(4, 12, 24, 0.55);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
}

/* The right-hand column (see sailor-main.js): the top-down course mini-map, then - only while the Ghost is
   tacking (upwind) or gybing (downwind) - a pill that flashes green (to starboard) or red (to port), see
   flashPill(). It is hidden the rest of the time. */
.ghost-overlay {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 4;
  width: 11rem;
  max-width: calc(50% - 1.1rem);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* How far ahead of or behind the player the Ghost boat is right now - the same ahead/behind framing as the
   post-race finish message (see describeEnd()), just live and phrased in time instead of distance. A dash until
   there's a live route to compare against. */
.ghost-gap {
  text-align: center;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--fg);
}

.ghost-pill {
  padding: 0.4rem 0.8rem;
  background: rgba(4, 12, 24, 0.55);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 999px;
  text-align: center;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(157, 180, 204, 0.7);
}

/* Shown only on the pre-race setup screen (see currentPhase()), in the same spot the results panel and the
   old sidebar occupied - a short welcome explaining what the simulator does, translucent so the course
   behind it still reads as the thing being explained. */
.intro-panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  width: 70%;
  max-width: calc(100% - 2rem);
  max-height: calc(100% - 2rem);
  overflow-y: auto;
  padding: 1.25rem 1.5rem;
  background: rgba(6, 16, 32, 0.84);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
  text-align: left;
}

.intro-panel h2 {
  margin: 0 0 0.6rem;
  color: var(--fg);
  text-align: center;
}

.intro-panel p {
  margin: 0.5rem 0;
  color: var(--fg);
  line-height: 1.5;
  font-size: 1.08rem;
  font-weight: 700;
}

.intro-panel p.intro-tagline {
  color: var(--accent);
  font-size: 1.2rem;
  text-align: center;
  margin: 1rem 0;
}

/* The Sailing Instructions screen (see sailor-main.js's renderInstructions()): shown between pressing Start
   Sailing and the run itself, in the same spot and with the same translucent look as the intro panel. Heading
   on top, the challenge's description / objectives (and an optional image on the right) in the middle, Back and
   Continue pinned to the bottom - only the middle scrolls if it is too tall. */
.instructions-panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  width: 80%;
  max-width: calc(100% - 2rem);
  max-height: calc(100% - 2rem);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1.25rem 1.5rem;
  background: rgba(6, 16, 32, 0.84);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
  text-align: left;
}

.instructions-panel[hidden] {
  display: none;
}

.instructions-kicker {
  margin: 0;
  text-align: center;
  color: var(--muted);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.instructions-panel h2 {
  margin: 0;
  color: var(--fg);
  text-align: center;
}

.instructions-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

/* No description written yet for this challenge and leg - a plain TBD, centred in the panel. */
.instructions-body.is-tbd {
  align-items: center;
  justify-content: center;
  min-height: 8rem;
}

.instructions-tbd {
  color: var(--muted);
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: 0.08em;
}

.instructions-text {
  flex: 1 1 0;
  min-width: 0;
  color: var(--fg);
}

.instructions-text p {
  margin: 0 0 0.9rem;
  line-height: 1.5;
  font-size: 1.02rem;
}

.instructions-text h3 {
  margin: 0 0 0.4rem;
  color: var(--muted);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.instructions-text ul {
  margin: 0;
  padding-left: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  line-height: 1.45;
  font-size: 1.02rem;
}

.instructions-image {
  flex: 0 0 38%;
  max-width: 38%;
  border-radius: 8px;
  object-fit: contain;
}

.instructions-actions {
  display: flex;
  justify-content: space-between;
  gap: 0.75rem;
}

.instructions-actions .btn {
  min-width: 6rem;
}

/* The Show Controls popup: the annotated race screen, as large as the window allows, with Continue under it. */
.controls-modal-content {
  width: auto;
  max-width: 96vw;
  max-height: 96vh;
  padding: 0.75rem;
  align-items: center;
  gap: 0.75rem;
}

/* Scrolls (both ways) only when the image is shown larger than the window - see the narrow-screen rule below -
   so Continue, outside it, always stays on screen. */
.controls-scroll {
  max-width: 100%;
  max-height: calc(96vh - 5.5rem);
  overflow: auto;
}

.controls-modal-content img {
  display: block;
  max-width: 100%;
  max-height: calc(96vh - 5.5rem);
  object-fit: contain;
  border-radius: 8px;
}

/* The top-down course view on the results screen: both boats' tracks on the whole sailing area, across the width
   of the page. The course is taller than wide, so the panel's height is capped (the page scrolls, so the cap sits
   above one screen): past it the course simply sits centred in the wider panel. Drawn flat rather than tilted back like the mini-map, so the
   tracks read at their true shape. */
.end-course {
  width: 100%;
  flex: none;
  aspect-ratio: 4 / 5;
  max-height: 110dvh;
}

/* The race plot on the left, taking most of the width, and "Where time was lost" in a narrower column to its
   right. A narrow (phone-portrait) page stacks them, the plot then running out to the edges of its card. */
.plot-row {
  display: grid;
  grid-template-columns: minmax(0, 2.4fr) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}

.plot-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.plot-row .loss-block h4 {
  margin-top: 0;
}

@media (max-width: 600px) {
  .plot-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.9rem;
  }

  .end-course {
    width: calc(100% + 2 * var(--card-pad-x, 0px));
    margin-inline: calc(-1 * var(--card-pad-x, 0px));
    border-radius: 0;
  }
}

.end-course svg {
  transform: none;
}

.course-label {
  fill: #b9cce0;
  font-size: 12px;
  font-weight: 600;
}

.frame {
  fill: none;
  stroke: rgba(255, 255, 255, 0.32);
  stroke-dasharray: 6 6;
}

.start-line {
  stroke: #f4f8fc;
  stroke-width: 2.5;
  stroke-dasharray: 9 7;
}

.pin {
  fill: #ffb703;
  stroke: #fff;
  stroke-width: 1.5;
}

.committee {
  fill: #f4f8fc;
  stroke: #9db4cc;
}

.mark {
  fill: #ffb703;
  stroke: #fff;
  stroke-width: 2;
}

.gate-line {
  stroke: #ffb703;
  stroke-width: 2;
  stroke-dasharray: 10 6;
  opacity: 0.6;
}

.finish-line {
  stroke: #f4f8fc;
  stroke-width: 2.5;
  stroke-dasharray: 9 7;
  opacity: 0.8;
}

/* The mini-map's wind arrows (see view.js's drawWindArrows): light and translucent, pointing the way the wind
   blows, fading in and out once per --wind-blink (set from the wind speed). They sit under everything else. */
.wind-arrow {
  fill: none;
  stroke: #dcecff;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  animation: wind-arrow-blink var(--wind-blink, 1.4s) ease-in-out infinite;
}

@keyframes wind-arrow-blink {
  0%, 100% { opacity: 0.1; }
  50% { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
  .wind-arrow { animation: none; opacity: 0.25; }
}

.optimal-track {
  fill: none;
  stroke: #ff9f1c;
  stroke-width: 3;
  stroke-linejoin: round;
  stroke-linecap: round;
  opacity: 0.9;
}

/* The Ghost boat's own live track and current-position marker (see CourseView's showTarget) - dashed, to
   read as "so far" rather than optimal-track's solid, complete route shown once a leg ends. */
.target-track {
  fill: none;
  stroke: #ff9f1c;
  stroke-width: 2.5;
  stroke-linejoin: round;
  stroke-linecap: round;
  stroke-dasharray: 6 5;
  opacity: 0.85;
}

.target-marker {
  fill: #ff9f1c;
  stroke: #fff;
  stroke-width: 1.5;
}

.track {
  fill: none;
  stroke: rgba(28, 168, 221, 0.7);
  stroke-width: 2;
  stroke-linejoin: round;
}

.boat-hull {
  fill: #f4f8fc;
  stroke-width: 2.5;
  stroke-linejoin: round;
}

.boat-hull.starboard {
  stroke: #4ade80;
}

.boat-hull.port {
  stroke: #ff6b6b;
}

.boat-sail {
  stroke: #0a2a4c;
  stroke-width: 2.5;
  stroke-linecap: round;
}

/* Sits on the course view itself, in the letterboxed space outside the dashed sailing-area frame (the course's
   own aspect ratio rarely matches the plot container's, so that margin is normally free) rather than in a
   separate sidebar - freeing that width for the course. A semi-opaque background keeps it readable over the
   course either way. */
.hud {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 4;
  width: 13rem;
  max-width: calc(100% - 1.5rem);
  max-height: calc(100% - 1.5rem);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.75rem;
  background: rgba(6, 16, 32, 0.78);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
  text-align: left;
}

.wind {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.rose {
  flex: none;
  width: 6.75rem;
}

.rose-ring {
  fill: rgba(4, 12, 24, 0.55);
  stroke: #9db4cc;
  stroke-width: 1.5;
}

.rose-letters {
  fill: #b9cce0;
}

.rose-arrow {
  stroke: var(--accent);
}

.rose-arrow-head {
  fill: var(--accent);
}

.wind-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.hud-label {
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.wind-direction {
  font-size: 1.7rem;
  font-weight: 700;
}

.wind-speed {
  font-size: 1.05rem;
  color: var(--fg);
}

.charts {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.chart {
  background: rgba(4, 12, 24, 0.35);
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
  overflow: hidden;
}

/* Two scrolling strip charts (see sailor-main.js's renderWindTrend()): time runs down the y-axis, newest
   reading at the top, each sample sinking toward the bottom as it ages off. Direction (left) is plotted
   relative to the wind at the start of the race - a centreline at 0 - and coloured by which tack the boat was
   on at each sample; speed (right) runs left (0 kt) to right. */
.wind-trend-charts {
  display: flex;
  gap: 0.4rem;
}

.wind-trend-svg {
  flex: 1;
  min-width: 0;
  display: block;
  aspect-ratio: 72 / 150;
  background: rgba(4, 12, 24, 0.35);
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
}

.wind-trend-title {
  fill: #b9cce0;
  font-size: 7px;
  font-weight: 600;
}

.wind-trend-value {
  fill: var(--fg);
  font-size: 7px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.wind-trend-tick {
  fill: var(--muted);
  font-size: 6px;
  font-variant-numeric: tabular-nums;
}

.wind-trend-axis {
  stroke: rgba(157, 180, 204, 0.35);
  stroke-width: 0.6;
}

.wind-trend-center {
  stroke: rgba(157, 180, 204, 0.45);
  stroke-width: 0.6;
  stroke-dasharray: 2 2;
}

.wind-trend-line {
  fill: none;
  stroke-width: 1.6;
  stroke-linecap: round;
}

.wind-trend-line.starboard {
  stroke: #39ff6a;
}

.wind-trend-line.port {
  stroke: #ff3b3b;
}

.wind-trend-line.speed {
  stroke: #7fe8ff;
}

.stats {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.35rem 0.75rem;
  margin: 0;
  padding: 0.65rem 0.8rem;
  font-size: 0.82rem;
  background: rgba(4, 12, 24, 0.35);
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
}

.stats dt {
  color: var(--muted);
}

.stats dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.lbl-short {
  display: none;
}

.challenge-info {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
}

.challenge-info strong {
  color: var(--fg);
}

.end-report-text {
  margin: 0;
  color: var(--muted);
  line-height: 1.4;
  font-size: 0.85rem;
}

.end-stats {
  display: block;
  overflow-x: auto;
  margin: 0;
  padding: 0.6rem 0.75rem;
  background: rgba(4, 12, 24, 0.35);
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
  font-size: 0.9rem;
  border-collapse: collapse;
  width: 100%;
}

.end-stats th,
.end-stats td {
  padding: 0.4rem 0.6rem;
  white-space: nowrap;
}

.end-stats thead th {
  color: var(--muted);
  font-weight: 600;
  font-size: 0.8rem;
  text-align: right;
  border-bottom: 1px solid rgba(157, 180, 204, 0.2);
}

.end-stats thead th:first-child {
  text-align: left;
}

.end-stats tbody th {
  text-align: left;
  color: var(--muted);
  font-weight: 400;
}

.end-stats tbody td {
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.end-stats tbody td:last-child {
  color: #ff9f1c;
}

/* The results screen: fills the plot once a run ends, as one scrolling page - the objectives summary, then the race
   plot across the full width, then the wind-shift chart. The Exit button in the control bar below stays visible. */
.results-screen {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.9rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  background: linear-gradient(180deg, #0d2f52, #08203b);
  outline: none;
}

.results-screen > * {
  flex: none;
}

.results-summary {
  flex: none;
  padding: 1rem 1.5rem;
  text-align: center;
  background: rgba(9, 22, 40, 0.78);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 12px;
}

.summary-headline {
  margin: 0;
  font-size: clamp(1.5rem, 4vw, 2.4rem);
  line-height: 1.15;
  color: var(--fg);
}

.results-summary[data-tier="perfect"] {
  border-color: #4ade80;
}

.results-summary[data-tier="perfect"] .summary-headline {
  color: #4ade80;
}

.results-summary[data-tier="good"] {
  border-color: #fbbf24;
}

.results-summary[data-tier="good"] .summary-headline {
  color: #fbbf24;
}

.results-summary[data-tier="low"] {
  border-color: #ff8a65;
}

.results-summary[data-tier="low"] .summary-headline {
  color: #ff8a65;
}

.results-summary .summary-detail {
  margin: 0.4rem 0 0;
  font-size: 1.05rem;
  color: var(--fg);
}

.results-card {
  --card-pad-x: 1.5rem;
  width: 100%;
  padding: 1.25rem var(--card-pad-x);
  background: rgba(9, 22, 40, 0.78);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(3px);
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.results-card > h3 {
  margin: 0;
  font-size: 1rem;
  color: var(--fg);
}

/* .end-stats's own cells are nowrap, so a value like "1,157 m (0.62 nm)" forces the table wider than a
   narrow results-card and cuts off the second column instead of wrapping. */
.results-card .end-stats th,
.results-card .end-stats td {
  white-space: normal;
}

/* The challenge's objectives with their verdicts (see objectives.js), under the comparison table: a tick for
   met, a cross for missed, a dash for one the simulator cannot measure - each with the measured figure under
   it, so a verdict is never just a mark. */
.objectives {
  text-align: left;
}

.objectives h3 {
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
  color: var(--fg);
}

.objectives-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.objectives-list li {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
}

.objective-mark {
  flex: none;
  width: 1.1rem;
  font-weight: 800;
  text-align: center;
  color: var(--muted);
}

.objective-met .objective-mark {
  color: #4ade80;
}

.objective-missed .objective-mark {
  color: #ff6b6b;
}

.objective-text {
  display: block;
  color: var(--fg);
  font-size: 0.85rem;
  line-height: 1.35;
}

.objective-detail {
  display: block;
  margin-top: 0.1rem;
  color: var(--muted);
  font-size: 0.75rem;
}


/* The right-hand column: every tack or gybe both boats made, in time order. */
.event-log-panel {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1rem 1.1rem;
  background: rgba(9, 22, 40, 0.78);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
}

.event-log-panel h3 {
  margin: 0;
  font-size: 0.95rem;
  color: var(--fg);
  flex: 0 0 auto;
}

.event-log-panel .end-stats {
  font-size: 0.78rem;
}

.event-log-panel .end-stats th,
.event-log-panel .end-stats td {
  white-space: normal;
}

/* The event log reuses .end-stats for the card/background treatment, but every column here is a plain text
   value (a timestamp, a boat name, a maneuver) rather than a row label next to right-aligned numbers, so the
   numeric-table defaults (right alignment, bold, tabular figures, the last column's orange "best" colour) are
   turned back off. */
.event-log th,
.event-log td {
  text-align: left;
  font-weight: 400;
}

.event-log tbody td:last-child {
  color: var(--fg);
}

.game-message {
  min-height: 1.2em;
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
}

.game-controls {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
}

.game-controls .btn {
  padding: 0.45rem 1rem;
  font-size: 0.9rem;
  min-width: 4.5rem;
}

.game-controls .btn:disabled {
  opacity: 0.4;
  cursor: default;
}

.control-select {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--muted);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.control-select select {
  padding: 0.4rem 0.5rem;
  border-radius: 6px;
  border: 1px solid var(--muted);
  background: var(--surface);
  color: var(--fg);
  font-size: 0.9rem;
  text-transform: none;
  letter-spacing: 0;
}

/* A labelled cluster of small buttons within the during-race controls - the speed choices and the steering
   nudges - set apart from the plain action buttons around them as its own grouped control. */
.control-panel {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.55rem;
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 8px;
  background: rgba(4, 12, 24, 0.25);
}

.control-panel-label {
  /* Stays put in the middle of the Helm group while the Down/Up buttons either side of it swap order to
     match the boat's tack (see sailor-main.js's syncControls()). */
  order: 2;
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-right: 0.1rem;
}

/* A fixed width, rather than the plain .control-panel's shrink-to-fit one, so Down/Helm/Up (see
   .control-panel-label above) have a stable row to spread evenly across via justify-content instead of
   bunching at the left - and so Best VMG's own flex-basis: 100% (see .btn-best-vmg) resolves to the same
   width on the line below, instead of both being caught in a circular auto-sizing dependency. */
.control-panel-helm {
  width: 12.5rem;
  justify-content: space-between;
}

.game-controls .btn-toggle,
.game-controls .btn-steer {
  padding: 0.35rem 0.6rem;
  font-size: 0.82rem;
  min-width: 2.4rem;
}

/* The speed buttons get the same treatment as the Helm group: a fixed-width box, the buttons spread evenly
   across it, and a caption forced onto its own line beneath them (flex-basis: 100%, like .btn-best-vmg). */
.control-panel-speed {
  width: 13.5rem;
  justify-content: space-between;
}

.control-panel-caption {
  order: 5;
  flex: 1 1 100%;
  text-align: center;
  color: var(--muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Tack/Gybe is the most important control in the run, so it is the one filled, larger button in the bar. */
.game-controls .btn-tack {
  min-width: 6rem;
  min-height: 3.4rem;
  padding: 0.7rem 1.2rem;
  font-size: 1.2rem;
  font-weight: 800;
}

/* Sits beneath Down/Helm/Up in the same grouped panel (see .control-panel's own flex-wrap), forced onto its
   own line and spanning the full width of the group above it via flex-basis: 100%. */
.btn-best-vmg {
  order: 4;
  flex: 1 1 100%;
}

.btn-toggle.active {
  background: var(--accent);
  color: #04121f;
  border-color: var(--accent);
}

@media (max-width: 760px) {
  .hud {
    width: 11rem;
    padding: 0.6rem;
    gap: 0.45rem;
  }

  .left-hud {
    width: 6rem;
  }

  .wind-dir-overlay {
    padding: 0.35rem;
  }

  .wind-overlay {
    padding: 0.4rem 0.5rem;
    gap: 0.3rem;
  }

  .ghost-overlay {
    width: 6rem;
  }

      .ghost-gap {
    font-size: 0.65rem;
  }

  .ghost-pill {
    padding: 0.35rem 0.5rem;
    font-size: 0.6rem;
  }

  .intro-panel {
    padding: 1rem 1.1rem;
  }

  .intro-panel p {
    font-size: 0.98rem;
  }

  .instructions-panel {
    width: calc(100% - 1.5rem);
    padding: 1rem 1.1rem;
  }

  /* The annotated controls image is mostly small text: shrunk to fit a phone it would be about a fifth of its
     size and unreadable, so here it keeps a readable width and the popup pans instead (see .controls-scroll). */
  .controls-modal-content img {
    max-width: none;
    max-height: none;
    width: 820px;
  }

  .instructions-body {
    flex-direction: column;
  }

  .instructions-image {
    flex: none;
    max-width: 100%;
  }

  /* Small screens stack the three results columns one under another; the panel scrolls. */
  .results-screen {
    padding: 0.6rem;
    gap: 0.6rem;
  }

  .results-summary {
    padding: 0.7rem 0.9rem;
  }

  .results-summary .summary-detail {
    font-size: 0.9rem;
  }

  .results-card {
    --card-pad-x: 1rem;
    padding: 1rem var(--card-pad-x);
    gap: 0.7rem;
  }

  .event-log-panel {
    padding: 0.8rem 1rem;
  }

  .rose {
    width: 4.5rem;
  }

  .wind-direction {
    font-size: 1.2rem;
  }

  .wind-speed {
    font-size: 0.85rem;
  }

  .stats {
    gap: 0.25rem 0.5rem;
    padding: 0.4rem 0.5rem;
    font-size: 0.72rem;
  }

  .hide-narrow {
    display: none;
  }

  .lbl-long {
    display: none;
  }

  .lbl-short {
    display: inline;
  }
}

/* ---- Accounts and scores (auth.js, reports.js) -------------------------------------------------------------------- */
.user-status.is-link {
  cursor: pointer;
}

.user-status.is-link:hover {
  color: var(--fg);
  text-decoration: underline;
}

.modal-content.modal-wide {
  max-width: 27rem;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
}

.modal-note {
  margin: 0;
  color: var(--muted);
  font-size: 0.85rem;
}

.field-hint {
  color: var(--muted);
  font-size: 0.75rem;
  opacity: 0.8;
}

.modal-content input[name="team_name"] {
  text-transform: uppercase;
}

.avatar-field {
  border: none;
  margin: 0;
  padding: 0;
}

.avatar-field legend {
  color: var(--muted);
  font-size: 0.85rem;
  padding: 0;
  margin-bottom: 0.35rem;
}

.avatar-picker {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 0.4rem;
}

.avatar-option {
  position: relative;
  cursor: pointer;
}

.avatar-option input {
  position: absolute;
  opacity: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.avatar-option span {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  font-size: 1.5rem;
  border-radius: 8px;
  border: 2px solid transparent;
  background: rgba(255, 255, 255, 0.05);
}

.avatar-option input:checked + span {
  border-color: var(--accent);
  background: rgba(28, 168, 221, 0.18);
}

.avatar-option input:focus-visible + span {
  outline: 2px solid var(--fg);
}

.tab-row {
  display: flex;
  gap: 0.4rem;
}

.tab {
  padding: 0.4rem 1rem;
  border-radius: 999px;
  border: 1px solid rgba(157, 180, 204, 0.4);
  background: transparent;
  color: var(--muted);
  font: inherit;
  cursor: pointer;
}

.tab[aria-selected="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: #04121f;
  font-weight: 600;
}

.objective-bars .bar-row {
  grid-template-columns: minmax(10rem, 1fr) 8rem 6.5rem;
}

.score-save-note {
  margin: 0.75rem 0 0;
  color: var(--muted);
  font-size: 0.8rem;
}

.score-save-note:empty {
  display: none;
}

/* ---- Admin and Account pages (admin.js, account.js) -------------------------------------------------------------- */
.btn-danger {
  background: #c0392b;
  color: #fff;
  border: 1px solid #c0392b;
}

.btn-danger:hover:not(:disabled) {
  background: #d9473a;
}

.btn:disabled {
  opacity: 0.6;
  cursor: default;
}

/* A fixed-height, scrolling list with its column headings pinned. */
.table-scroll {
  max-height: 26rem;
  overflow: auto;
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
}

.table-scroll .admin-table thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 1;
}

.table-scroll .admin-table tbody tr:last-child td {
  border-bottom: none;
}

.muted {
  color: var(--muted);
}

.badge {
  margin-left: 0.4rem;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  background: rgba(28, 168, 221, 0.2);
  color: var(--accent);
  font-size: 0.7rem;
}

.modal-actions {
  display: flex;
  gap: 0.75rem;
  justify-content: flex-end;
}

.modal-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0;
  color: var(--muted);
  font-size: 0.8rem;
}

.modal-divider::before,
.modal-divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid rgba(157, 180, 204, 0.25);
}

.account-main {
  max-width: 36rem;
}

.account-card {
  padding: 1.25rem 1.5rem;
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 10px;
  background: var(--surface);
}

.account-card.danger-card {
  border-color: rgba(192, 57, 43, 0.5);
}

.stack-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.stack-form label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.stack-form input[type="text"],
.stack-form input[type="password"] {
  padding: 0.6rem 0.7rem;
  border-radius: 6px;
  border: 1px solid rgba(157, 180, 204, 0.3);
  background: rgba(255, 255, 255, 0.05);
  color: var(--fg);
  font-size: 1rem;
}

.stack-form input[name="team_name"] {
  text-transform: uppercase;
}

.stack-form .btn {
  align-self: flex-start;
}

.stack-form .modal-actions .btn {
  align-self: auto;
}

/* On a narrow screen the Delete button stays visible while the rest of the row scrolls. */
.table-scroll .admin-table th:last-child,
.table-scroll .admin-table td:last-child {
  position: sticky;
  right: 0;
  background: var(--surface);
  box-shadow: -6px 0 8px -6px rgba(0, 0, 0, 0.5);
}

/* Banners (index.html) that can be dismissed. */
.banner-close {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 0.25rem;
}

.banner-close:hover {
  color: var(--fg);
}

/* Hidden while a run is under way, and on the start page (see sailor-main.js's syncControls()), so the course
   has the whole screen and the start page stands alone. */
.is-racing .topbar,
.is-racing .banner,
.is-intro .topbar,
.is-intro .banner {
  display: none;
}

/* One bullet per objective in the results summary, centred as a block under the headline. */
.summary-objectives {
  width: fit-content;
  max-width: 100%;
  margin: 0.8rem auto 0;
  text-align: left;
  gap: 0.45rem;
}

.summary-objectives .objective-text {
  font-size: 0.95rem;
}

.summary-objectives .objective-detail {
  font-size: 0.8rem;
}

/* ---- Phones: touch targets, landscape layout, compact panels ---------------------------------------------------- */
.hud-toggle,
.rotate-hint,
.btn-speed-cycle {
  display: none;
}

.btn-steer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
}

.btn-icon {
  width: 0.85em;
  height: 0.85em;
  flex: none;
}

.hud-toggle {
  font: inherit;
  cursor: pointer;
}

/* Fingers rather than a pointer: bigger targets, no double-tap zoom or text-selection / context menu on the
   controls that are pressed and held. */
@media (pointer: coarse) {
  .game-controls button,
  .hud-toggle {
    touch-action: manipulation;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }

  .game-controls .btn {
    min-height: 2.9rem;
    font-size: 1rem;
  }

  .game-controls .btn-toggle,
  .game-controls .btn-steer {
    min-width: 2.9rem;
    min-height: 2.9rem;
    font-size: 0.95rem;
  }

  .game-controls .btn-tack {
    min-width: 8rem;
    min-height: 4rem;
    font-size: 1.4rem;
  }

  .control-panel-helm {
    width: 14rem;
  }

  .control-panel-speed {
    width: 16rem;
  }

  .control-select select {
    min-height: 2.6rem;
    font-size: 1rem;
  }

  .banner-close {
    min-width: 2.5rem;
    min-height: 2.5rem;
  }
}

/* A phone held upright is a narrow, tall window for a wide view. */
@media (orientation: portrait) and (max-width: 600px) and (pointer: coarse) {
  .rotate-hint {
    display: flex;
  }
}

/* Small screens, upright or sideways: the left-hand panels shrink to the wind dial (a small button opens the wind
   history), the mini-map and Ghost card slim down, and the results columns become tabs. */
@media (max-width: 600px), (orientation: landscape) and (max-height: 540px) {
  /* A small button under the elapsed-time label that opens the wind history, which then drops in below it. */
  .hud-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    order: 1;
    align-self: flex-start;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    border: 1px solid rgba(157, 180, 204, 0.4);
    background: rgba(4, 12, 24, 0.6);
    color: var(--fg);
    font-size: 1.2rem;
    line-height: 1;
  }

  .left-hud {
    width: 5.2rem;
  }

  .left-hud .wind-overlay {
    display: none;
  }

  .left-hud.expanded {
    width: 8.5rem;
  }

  .left-hud.expanded .wind-overlay {
    display: flex;
  }

  .ghost-overlay {
    width: 5.6rem;
  }

  .results-summary {
    padding: 0.5rem 0.7rem;
  }

  .summary-headline {
    font-size: 1.4rem;
  }

  .summary-objectives .objective-text {
    font-size: 0.82rem;
  }

  .summary-objectives .objective-detail {
    font-size: 0.7rem;
  }

  .intro-panel h2 {
    font-size: 1.3rem;
  }

  .intro-panel p {
    font-size: 0.9rem;
  }

  .intro-panel p.intro-tagline {
    font-size: 1rem;
  }

}

/* A phone held sideways: the course gets the whole screen, the controls float over it at the edges where thumbs
   rest (helm bottom left, tack and speed bottom right), and the results are one scrolling page. */
@media (orientation: landscape) and (max-height: 540px) {
  .topbar {
    padding: 0.2rem max(0.75rem, env(safe-area-inset-right)) 0.2rem max(0.75rem, env(safe-area-inset-left));
    gap: 0.2rem 0.75rem;
  }

  .topbar .nav-brand {
    font-size: 0.8rem;
    margin-right: 0.4rem;
  }

  .topbar .nav-link {
    font-size: 0.78rem;
    padding: 0.15rem 0.1rem;
  }

  .topbar .btn {
    padding: 0.2rem 0.6rem;
  }

  .banner {
    padding: 0.3rem 0.75rem;
    font-size: 0.8rem;
  }

  .game-main {
    padding: 0.3rem max(0.5rem, env(safe-area-inset-right)) 0.4rem max(0.5rem, env(safe-area-inset-left));
    gap: 0.3rem;
  }

  .challenge-info {
    display: none;
  }

  .left-hud {
    top: 0.5rem;
    left: 0.5rem;
  }

  .ghost-overlay {
    top: 0.5rem;
    right: 0.5rem;
  }

  .intro-panel {
    width: 80%;
    padding: 0.6rem 1rem;
  }

  .intro-panel h2 {
    font-size: 1.2rem;
    margin: 0 0 0.3rem;
  }

  .intro-panel p {
    font-size: 0.85rem;
    margin: 0.3rem 0;
  }

  .intro-panel p.intro-tagline {
    font-size: 0.95rem;
    margin: 0.5rem 0;
  }

  .instructions-panel {
    width: calc(100% - 1rem);
    padding: 0.6rem 1rem;
  }

  /* During the race the controls leave the page flow and float over the course. */
  #duringRaceControls {
    position: fixed;
    inset: 0;
    z-index: 8;
    display: block;
    pointer-events: none;
  }

  #duringRaceControls > *,
  #duringRaceControls .btn {
    pointer-events: auto;
  }

  #duringRaceControls .btn-tack {
    position: absolute;
    right: max(0.75rem, env(safe-area-inset-right));
    bottom: max(0.75rem, env(safe-area-inset-bottom));
    min-width: 7.5rem;
    min-height: 4.2rem;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  }

  #duringRaceControls .control-panel-speed {
    position: absolute;
    right: max(0.75rem, env(safe-area-inset-right));
    bottom: calc(max(0.75rem, env(safe-area-inset-bottom)) + 5rem);
    width: 12.5rem;
    background: rgba(4, 12, 24, 0.6);
  }

  #duringRaceControls .control-panel-caption {
    display: none;
  }

  #duringRaceControls .control-panel-helm {
    position: absolute;
    left: max(0.75rem, env(safe-area-inset-left));
    bottom: max(0.75rem, env(safe-area-inset-bottom));
    width: 12.5rem;
    background: rgba(4, 12, 24, 0.6);
  }

  #duringRaceControls #pauseBtn {
    position: absolute;
    bottom: max(0.6rem, env(safe-area-inset-bottom));
    left: calc(50% - 1.6rem);
    min-width: 3.2rem;
    background: rgba(4, 12, 24, 0.6);
  }

  .results-screen {
    padding: 0.5rem;
    gap: 0.5rem;
  }

  .results-summary {
    padding: 0.6rem 0.8rem;
  }

  .summary-headline {
    font-size: 1.25rem;
  }

  .results-summary .summary-detail {
    font-size: 0.85rem;
  }

  .summary-objectives {
    margin-top: 0.5rem;
  }

  .summary-objectives .objective-text {
    font-size: 0.78rem;
  }

  .summary-objectives .objective-detail {
    font-size: 0.68rem;
  }

}

/* A phone held sideways in a browser that keeps its toolbar showing (Safari's) leaves only about 280-340 px of
   height. The controls shrink to fit without overlapping: helm Down / Best VMG / Up in one row bottom left, and
   along the bottom right a Pause button, one speed button (tap to step through the speeds) and
   a smaller Tack; the corner panels shrink too. */
@media (orientation: landscape) and (max-height: 360px) {
  .left-hud {
    width: 4.4rem;
  }

  .left-hud.expanded {
    width: 8rem;
  }

  .ghost-overlay {
    width: 4.6rem;
  }

  #duringRaceControls .control-panel-speed {
    display: none;
  }

  #duringRaceControls .btn-speed-cycle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    right: calc(max(0.5rem, env(safe-area-inset-right)) + 7rem);
    bottom: max(0.5rem, env(safe-area-inset-bottom));
    min-width: 3.2rem;
    min-height: 3.4rem;
    padding: 0;
    background: rgba(4, 12, 24, 0.6);
  }

  #duringRaceControls .btn-tack {
    right: max(0.5rem, env(safe-area-inset-right));
    bottom: max(0.5rem, env(safe-area-inset-bottom));
    min-width: 6.5rem;
    min-height: 3.4rem;
    font-size: 1.2rem;
  }

  #duringRaceControls .control-panel-helm {
    left: max(0.5rem, env(safe-area-inset-left));
    bottom: max(0.5rem, env(safe-area-inset-bottom));
    width: 13rem;
    flex-wrap: nowrap;
    padding: 0.25rem;
    gap: 0.25rem;
  }

  #duringRaceControls .control-panel-label {
    display: none;
  }

  #duringRaceControls .btn-best-vmg {
    order: 2;
    flex: 1 1 auto;
  }

  #duringRaceControls .control-panel-helm .btn {
    min-width: 0;
    min-height: 2.6rem;
    padding: 0 0.4rem;
    font-size: 0.82rem;
  }

  /* Beside the speed button on the right, clear of the instrument pod, which sits low and left of centre. */
  #duringRaceControls #pauseBtn {
    left: auto;
    right: calc(max(0.5rem, env(safe-area-inset-right)) + 10.8rem);
    bottom: max(0.5rem, env(safe-area-inset-bottom));
    width: 2.8rem;
    min-width: 2.8rem;
    height: 2.8rem;
    min-height: 2.8rem;
    padding: 0;
  }

  .intro-panel {
    padding: 0.4rem 0.8rem;
  }

  .intro-panel h2 {
    font-size: 1rem;
    margin: 0 0 0.15rem;
  }

  .intro-panel p {
    font-size: 0.74rem;
    margin: 0.15rem 0;
    line-height: 1.3;
  }

  .intro-panel p.intro-tagline {
    font-size: 0.82rem;
    margin: 0.25rem 0;
  }

  .instructions-panel {
    padding: 0.4rem 0.8rem;
  }

  /* The setup row on one line: the touch-sized selects and buttons would otherwise stack it into three. */
  #preRaceControls {
    flex-wrap: nowrap;
    gap: 0.4rem;
  }

  #preRaceControls .control-select {
    font-size: 0.62rem;
    gap: 0.25rem;
  }

  #preRaceControls .control-select select {
    min-height: 2.2rem;
    font-size: 0.85rem;
    padding: 0.2rem 0.4rem;
  }

  #preRaceControls .btn,
  #pausedControls .btn,
  #endedControls .btn {
    min-width: 0;
    min-height: 2.3rem;
    padding: 0.2rem 0.9rem;
    font-size: 0.85rem;
  }
}

.prose-build {
  margin-top: 2rem;
  color: var(--muted);
  font-size: 0.75rem;
}

/* The three buttons on the start screen's intro panel. */
.intro-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
  margin-top: 0.9rem;
}

.intro-panel .intro-actions .btn {
  min-height: 2.6rem;
  padding: 0.5rem 1.1rem;
}

@media (orientation: landscape) and (max-height: 360px) {
  .intro-actions {
    margin-top: 0.35rem;
    gap: 0.4rem;
  }

  .intro-panel .intro-actions .btn {
    min-height: 2.2rem;
    padding: 0.2rem 0.8rem;
    font-size: 0.82rem;
  }
}

/* The "get ready" countdown before a Boot Camp leg (see sailor-main.js's startCountdown()): a translucent panel
   like the intro, with the message and a large number that counts down from 3; it fades as the run begins. */
.countdown-panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  width: min(34rem, calc(100% - 2rem));
  max-height: calc(100% - 2rem);
  overflow-y: auto;
  padding: 1.1rem 1.5rem;
  text-align: center;
  background: rgba(6, 16, 32, 0.76);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
  transition: opacity 0.4s ease;
}

.countdown-panel.is-leaving {
  opacity: 0;
}

.countdown-kicker {
  margin: 0;
  color: var(--muted);
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.countdown-panel h2 {
  margin: 0.2rem 0 0.5rem;
  color: var(--fg);
}

.countdown-message {
  margin: 0;
  color: var(--fg);
  font-size: 1.05rem;
  line-height: 1.45;
}

.countdown-number {
  margin-top: 0.4rem;
  color: var(--accent);
  font-size: clamp(3.5rem, 12vw, 5.5rem);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .countdown-panel {
    transition: none;
  }
}

@media (orientation: landscape) and (max-height: 540px) {
  .countdown-panel {
    padding: 0.5rem 1rem;
  }

  .countdown-panel h2 {
    font-size: 1.05rem;
    margin: 0.1rem 0 0.3rem;
  }

  .countdown-message {
    font-size: 0.85rem;
    line-height: 1.3;
  }

  .countdown-number {
    margin-top: 0.2rem;
    font-size: 2.6rem;
  }
}

/* ---- Boot Camp tips (sim/tour.js) ---------------------------------------------------------------------------- */
.tour-overlay {
  position: fixed;
  inset: 0;
  z-index: 9;
  pointer-events: none;
}

.tour-arrow {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 0.4s ease;
  overflow: visible;
}

.tour-arrow line {
  stroke: #ffd23f;
  stroke-width: 3.5;
  stroke-linecap: round;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
}

.tour-message {
  --tour-dy: 0px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, calc(-50% + var(--tour-dy)));
  width: min(22rem, calc(100% - 2rem));
  padding: 0.9rem 1.2rem 1rem;
  text-align: center;
  background: rgba(6, 16, 32, 0.9);
  border: 1px solid rgba(255, 210, 63, 0.55);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity 0.4s ease;
}

.tour-overlay.is-visible .tour-message,
.tour-overlay.is-visible .tour-arrow {
  opacity: 1;
}

.tour-message h3 {
  margin: 0 1.6rem 0.25rem;
  color: #ffd23f;
  font-size: 1.25rem;
}

.tour-message p {
  margin: 0;
  color: var(--fg);
  font-size: 0.92rem;
  line-height: 1.4;
}

/* Stops the tips for good: a circled stop square at the message's corner. */
.tour-stop {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  width: 2.2rem;
  height: 2.2rem;
  padding: 0.25rem;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: #ff6b6b;
  cursor: pointer;
  pointer-events: auto;
  touch-action: manipulation;
}

/* OK: on to the next tip. Like the stop button, it takes clicks although the rest of the overlay does not. */
.tour-ok {
  margin-top: 0.7rem;
  min-width: 5rem;
  pointer-events: auto;
  touch-action: manipulation;
}

.tour-stop:hover {
  background: rgba(255, 107, 107, 0.16);
}

.tour-stop svg {
  width: 100%;
  height: 100%;
  display: block;
}

@media (prefers-reduced-motion: reduce) {
  .tour-message,
  .tour-arrow {
    transition: none;
  }
}

@media (orientation: landscape) and (max-height: 540px) {
  .tour-message {
    width: min(20rem, calc(100% - 2rem));
    padding: 0.5rem 1rem 0.6rem;
  }

  .tour-message h3 {
    font-size: 1.05rem;
  }

  .tour-message p {
    font-size: 0.8rem;
    line-height: 1.3;
  }

  .tour-ok {
    margin-top: 0.4rem;
    padding: 0.25rem 1rem;
    min-height: 0;
  }
}

/* The Admin page's learning-points table: the long text cells wrap instead of running off to the right, and the
   linked challenges are small chips. */
#learningTable td {
  white-space: normal;
  vertical-align: top;
}

#learningTable .lp-cell-wrap {
  min-width: 16rem;
}

#learningTable td:nth-child(5) {
  min-width: 15rem;
}

#learningTable .tag {
  display: inline-block;
  white-space: nowrap;
  margin: 0.1rem 0;
  padding: 0.1rem 0.55rem;
  border: 1px solid rgba(157, 180, 204, 0.4);
  border-radius: 999px;
  font-size: 0.8rem;
}

/* The Learning Points card (see sailor-main.js's renderLearning()): the golden rule the leg broke, what happened,
   and the rule's qualification. */
.learning-body {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  text-align: left;
}

.learning-body p {
  margin: 0;
}

.learning-body .lp-name {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

.learning-body .lp-rule {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--fg);
}

.learning-body .lp-evidence {
  font-size: 0.95rem;
  color: var(--fg);
}

.learning-body .lp-note {
  font-size: 0.85rem;
  color: var(--muted);
}

.learning-body .lp-note strong {
  color: var(--fg);
}

/* The results race plot's key and its list of where time was lost (see sim/analysis.js and view.js). */
.end-course .optimal-track {
  stroke-dasharray: 8 5;
}

.plot-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.9rem;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--muted);
  font-size: 0.75rem;
}

.plot-legend li {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.key-line {
  display: inline-block;
  width: 1.4rem;
  height: 0;
  border-top: 3px solid;
}

.key-line.you {
  border-color: rgba(28, 168, 221, 0.9);
}

.key-line.best {
  border-color: #ff9f1c;
  border-top-style: dashed;
}

.key-line.lost {
  border-color: #ff4d4d;
}

.loss-block h4 {
  margin: 0.2rem 0 0.4rem;
  font-size: 0.85rem;
  color: var(--fg);
}

.loss-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.78rem;
  color: var(--muted);
  text-align: left;
}

.loss-list li {
  display: flex;
  gap: 0.5rem;
  align-items: flex-start;
}

.loss-list .dot {
  flex: none;
  width: 0.7rem;
  height: 0.7rem;
  margin-top: 0.2rem;
  border-radius: 50%;
}

.loss-list strong {
  color: var(--fg);
  font-weight: 700;
}

/* The three time charts beside the race plot (sim/charts.js): stacked, sharing one time axis. */
.chart-card {
  gap: 0.4rem;
}

.chart-svg {
  display: block;
  width: 100%;
  height: auto;
}

.chart-title {
  fill: var(--fg);
  font-size: 11px;
  font-weight: 700;
}

.chart-legend-text {
  fill: var(--muted);
  font-size: 8.5px;
}

.chart-tick {
  fill: var(--muted);
  font-size: 8.5px;
}

.chart-axis-title {
  fill: var(--muted);
  font-size: 9px;
}

.chart-grid {
  stroke: rgba(157, 180, 204, 0.16);
  stroke-width: 1;
}

.chart-grid-x {
  stroke: rgba(157, 180, 204, 0.1);
}

.chart-zero {
  stroke: rgba(244, 248, 252, 0.7);
  stroke-width: 1.2;
}

.chart-frame {
  fill: none;
  stroke: rgba(157, 180, 204, 0.35);
  stroke-width: 1;
}

.chart-marker-text {
  font-size: 8px;
  font-weight: 700;
}

.results-summary .summary-efficiency {
  margin: 0.3rem 0 0;
  color: var(--accent);
  font-size: 1.05rem;
  font-weight: 700;
}

/* "Your best / Team best / All-time best" under the VMG Score, in the results summary and the end popup. */
.summary-bests {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.2rem 1rem;
  margin: 0.3rem 0 0;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--fg);
}

.summary-bests .bests-label {
  color: var(--muted);
  font-weight: 600;
}

/* Player levels (server/levels.py): the ladder on the Account page, and the level-up line on the results screen. */
.level-current {
  margin: 0 0 0.6rem;
  color: var(--accent);
  font-size: 1.3rem;
  font-weight: 800;
}

.level-ladder {
  margin: 0 0 0.8rem;
  padding-left: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  color: var(--muted);
  font-size: 0.85rem;
  text-align: left;
}

.level-ladder li.reached {
  color: var(--fg);
}

.level-ladder li.current {
  color: var(--accent);
}

.level-ladder li.current strong::after {
  content: " - you are here";
  font-weight: 400;
}

.level-requirements {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.85rem;
  color: var(--muted);
  text-align: left;
}

.level-requirements li.met {
  color: #4ade80;
}

.score-save-note.level-up {
  color: #ffd23f;
  font-size: 1rem;
  font-weight: 800;
}

/* Crew levels: the small level avatar and title beside the user name in the top bar, and the About page's table. */
.level-avatar {
  display: inline-block;
  vertical-align: middle;
  border-radius: 50%;
  flex: none;
}

.level-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-left: 0.2rem;
  padding: 0.1rem 0.5rem 0.1rem 0.15rem;
  border: 1px solid rgba(157, 180, 204, 0.35);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--fg);
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

.levels-tagline {
  color: var(--accent);
  font-weight: 700;
  font-size: 1.05rem;
}

.levels-table td {
  white-space: normal;
  vertical-align: middle;
}

.levels-table td:first-child {
  color: var(--muted);
  font-weight: 700;
  width: 2rem;
}

.takeaways-table td {
  white-space: normal;
  vertical-align: top;
}

.takeaways-table td:first-child {
  font-weight: 700;
  min-width: 9rem;
}

.takeaways-table td:nth-child(2) {
  min-width: 12rem;
}

.levels-table .level-role {
  white-space: nowrap;
}

.levels-table .level-role .level-avatar {
  margin-right: 0.6rem;
}

.level-current {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.level-ladder li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* The elapsed-time label at the top of the left-hand column of the sailing screen. */
.elapsed-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.3rem 0.5rem;
  background: rgba(4, 12, 24, 0.55);
  border: 1px solid rgba(157, 180, 204, 0.3);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
}

.elapsed-label {
  color: var(--muted);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.elapsed-value {
  color: var(--fg);
  font-size: 1.05rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

/* The end-of-race popup (index.html's endPopup): the objectives' results over the simulation screen, with Back and
   Details at the foot. */
.end-popup {
  position: fixed;
  inset: 0;
  z-index: 9;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(4, 12, 24, 0.35);
}

.end-popup-card {
  width: min(34rem, 100%);
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  padding: 1.1rem 1.4rem 1.2rem;
  text-align: center;
  background: rgba(9, 22, 40, 0.94);
  border: 1px solid rgba(157, 180, 204, 0.4);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
}

.end-popup-card[data-tier="perfect"] {
  border-color: #4ade80;
}

.end-popup-card[data-tier="good"] {
  border-color: #fbbf24;
}

.end-popup-card[data-tier="low"] {
  border-color: #ff8a65;
}

.end-popup-card[data-tier="perfect"] .summary-headline {
  color: #4ade80;
}

.end-popup-card[data-tier="good"] .summary-headline {
  color: #fbbf24;
}

.end-popup-card[data-tier="low"] .summary-headline {
  color: #ff8a65;
}

.end-popup-card .summary-detail {
  margin: 0.4rem 0 0;
  color: var(--fg);
  font-size: 1.05rem;
}

.end-popup-card .summary-efficiency {
  margin: 0.3rem 0 0;
  color: var(--accent);
  font-size: 1.05rem;
  font-weight: 700;
}

.end-popup-actions {
  display: flex;
  justify-content: center;
  gap: 0.8rem;
  margin-top: 1rem;
}

.end-popup-actions .btn {
  min-width: 7rem;
  min-height: 2.6rem;
}

@media (orientation: landscape) and (max-height: 540px) {
  .end-popup-card {
    padding: 0.6rem 1rem 0.7rem;
  }

  .end-popup-card .summary-headline {
    font-size: 1.25rem;
  }

  .end-popup-card .summary-bests {
    font-size: 0.75rem;
    gap: 0.1rem 0.7rem;
  }

  .end-popup-card .summary-detail,
  .end-popup-card .summary-efficiency {
    font-size: 0.85rem;
    margin-top: 0.2rem;
  }

  .end-popup-actions {
    margin-top: 0.6rem;
  }
}

/* Under the start screen's title: the signed-in player's name, avatar and level. */
.intro-panel .intro-user {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 0.6rem;
  color: var(--fg);
  font-size: 1.05rem;
  font-weight: 700;
}

.intro-panel .intro-user .level-chip {
  font-size: 0.9rem;
}

@media (orientation: landscape) and (max-height: 360px) {
  .intro-panel .intro-user {
    margin: 0 0 0.2rem;
    font-size: 0.85rem;
  }
}

/* The start screen's greeting (avatars.js's fillWelcome()): "Welcome <avatar> <name>" and "Crew Rank <level avatar> <title>". */
.intro-panel .intro-user {
  gap: 0.4rem 1.6rem;
}

.intro-user .welcome-name,
.intro-user .welcome-rank {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}

.intro-user .welcome-label {
  white-space: nowrap;
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The game page never scrolls as a whole: everything has to fit the visible screen, so on a phone nothing sits
   below the fold waiting for a drag (and the page does not bounce). */
html:has(body.game-page),
body.game-page {
  overflow: hidden;
  overscroll-behavior: none;
}

/* A phone held upright: the results page uses the whole screen above the Exit bar (no menu, banners or challenge
   line), and is one scrolling page (see .results-screen). */
@media (max-width: 600px) and (orientation: portrait) {
  body.results-open .topbar,
  body.results-open .banner,
  body.results-open .challenge-info {
    display: none;
  }

}

/* The wind history comes after the toggle in the left-hand column. */
.left-hud .wind-overlay {
  order: 2;
}

/* ---- My Logbook (reports.html / reports.js) ---------------------------------------------------------------------- */
/* The Crew Rank ladder: every rank left to right, the player's highlighted, those to come faded. */
.rank-ladder {
  padding: 1rem 1.25rem 1.1rem;
  border: 1px solid rgba(157, 180, 204, 0.2);
  border-radius: 8px;
  background: var(--surface);
}

.rank-ladder-title {
  margin: 0 0 0.7rem;
  color: var(--muted);
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.rank-steps {
  display: flex;
  justify-content: space-between;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
}

.rank-step {
  position: relative;
  flex: 1 0 4.2rem;
  min-width: 4.2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding-top: 0.2rem;
  text-align: center;
}

/* The line the ranks sit on, running from one badge to the next. */
.rank-step::before {
  content: "";
  position: absolute;
  top: 1.65rem;
  left: -50%;
  width: 100%;
  height: 3px;
  background: rgba(157, 180, 204, 0.25);
  z-index: 0;
}

.rank-step:first-child::before {
  display: none;
}

.rank-step.earned::before,
.rank-step.current::before {
  background: var(--accent);
}

.rank-badge {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.3rem;
  height: 3.3rem;
  border-radius: 50%;
  background: #0d2f52;
  border: 2px solid rgba(157, 180, 204, 0.3);
}

.rank-badge img {
  width: 2.7rem;
  height: 2.7rem;
  display: block;
}

.rank-step.ahead .rank-badge,
.rank-step.ahead .rank-name {
  opacity: 0.4;
}

.rank-step.earned .rank-badge {
  border-color: var(--accent);
}

.rank-step.current .rank-badge {
  border-color: #ffd23f;
  box-shadow: 0 0 0 4px rgba(255, 210, 63, 0.25), 0 0 16px rgba(255, 210, 63, 0.45);
}

.rank-name {
  color: var(--fg);
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.2;
}

.rank-step.current .rank-name {
  color: #ffd23f;
  font-weight: 800;
}

.rank-you {
  padding: 0.05rem 0.55rem;
  border-radius: 999px;
  background: #ffd23f;
  color: #04121f;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* The logbook: one row per challenge and leg - its name and best score, the best run's race plot, and where that
   run lost time. Challenges not completed show an icon across the last two cells. */
.logbook-table td {
  white-space: normal;
  vertical-align: top;
}

.logbook-name {
  min-width: 9rem;
}

.logbook-name strong {
  display: block;
  color: var(--fg);
}

.logbook-name .logbook-leg {
  display: block;
  color: var(--accent);
  font-weight: 700;
  font-size: 0.85rem;
}

.logbook-name .logbook-best {
  display: block;
  margin-top: 0.6rem;
  font-weight: 700;
}

.logbook-name .logbook-date {
  display: block;
  color: var(--muted);
  font-size: 0.8rem;
}

.logbook-plot-cell {
  width: 15rem;
}

.logbook-plot {
  width: 14rem;
  max-height: none;
}

.logbook-note {
  margin: 0.3rem 0 0;
  color: var(--muted);
  font-size: 0.75rem;
}

.logbook-loss h4 {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  color: var(--fg);
}

.logbook-table td.logbook-empty {
  text-align: center;
  vertical-align: middle;
  color: var(--muted);
}

.logbook-empty-icon {
  display: block;
  width: 3.3rem;
  margin: 0 auto 0.3rem;
}

.logbook-empty-text {
  display: block;
  font-weight: 600;
}

tr.not-completed .logbook-name strong,
tr.not-completed .logbook-name .logbook-leg {
  opacity: 0.7;
}

/* A narrow page stacks each row into a card: the name, then the plot, then where time was lost. */
@media (max-width: 700px) {
  .logbook-table thead {
    display: none;
  }

  .logbook-table,
  .logbook-table tbody,
  .logbook-table tr,
  .logbook-table td {
    display: block;
    width: 100%;
  }

  .logbook-table tr {
    padding: 0.6rem 0;
    border-bottom: 1px solid rgba(157, 180, 204, 0.2);
  }

  .logbook-table td {
    border: none;
    padding: 0.35rem 0.9rem;
  }

  .logbook-plot-cell {
    width: 100%;
  }

  .logbook-plot {
    width: min(14rem, 100%);
  }

  /* All seven ranks fit across a phone: smaller badges and names. */
  .rank-ladder {
    padding: 0.8rem 0.6rem 0.9rem;
  }

  .rank-ladder-title {
    padding-left: 0.4rem;
  }

  .rank-steps {
    overflow-x: visible;
    gap: 0.15rem;
  }

  .rank-step {
    flex: 1 1 0;
    min-width: 0;
  }

  .rank-step::before {
    top: 1.45rem;
  }

  .rank-badge {
    width: 2.6rem;
    height: 2.6rem;
  }

  .rank-badge img {
    width: 2.1rem;
    height: 2.1rem;
  }

  .rank-name {
    font-size: 0.55rem;
    letter-spacing: -0.01em;
  }
}
