/*
 * Fraunces, for the wordmark and the tier names. Self-hosted like the landing
 * page's Space Mono, so this page still makes no external requests.
 *
 * SIL Open Font License 1.1 — Copyright 2018 The Fraunces Project Authors
 * (https://github.com/undercasetype/Fraunces). The licence travels with the file
 * in assets/fonts/OFL-Fraunces.txt, which is what the OFL asks for.
 */
@font-face {
  font-family: "Fraunces";
  src: url("/assets/fraunces-600-latin-C0hXCaiC.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  --sg-bg: #05070e;
  /*
   * Panels are near-black and faintly translucent rather than a lifted blue, and
   * the rule between them is a light hairline rather than a navy border.
   *
   * The old ramp was bg / panel / panel-2 / line: four steps of one blue, a few
   * points apart. That is what made every surface on the quest read as a card
   * whatever shape it was, and flattening it here reaches all eleven panels and
   * twenty-three borders at once.
   */
  --sg-panel: rgba(8, 11, 21, 0.88);
  --sg-panel-2: rgba(14, 19, 33, 0.85);
  --sg-line: rgba(190, 210, 245, 0.18);
  --sg-text: #e6ecf8;
  --sg-muted: #8a99b8;
  --sg-accent: #7fb2ff;
  --sg-guess: #ffcf5c;
  --sg-actual: #6ee7a8;
}

/*
 * The colour-blind-safe pin pair.
 *
 * Amber against green is the single worst choice for the two commonest forms of
 * colour blindness — red-green, between them about 8% of men — and the guess pin
 * and the answer pin are exactly the two marks a player has to tell apart. Both
 * colours are from Okabe and Ito's palette, which is designed to stay distinct
 * under protanopia, deuteranopia and tritanopia: the guess stays warm and the
 * answer moves from green to sky blue, which is the move that fixes it.
 *
 * Every consumer already reads these two variables, so redefining them on the
 * page reaches the map pins, the answer line and the readouts at once.
 */
.sg-page.is-colourblind {
  --sg-guess: #e69f00;
  --sg-actual: #56b4e9;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--sg-bg);
  color: var(--sg-text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
}

.sg-page {
  max-width: 1080px;
  margin: 0 auto;
  padding: 28px 20px 64px;
}

/*
 * While a round is up the page is pinned to the viewport instead of scrolling.
 * The sky is the thing worth looking at, so it takes every pixel left over and
 * the guess panel slides in over it.
 */
.sg-page.is-playing {
  max-width: none;
  height: 100vh;
  height: 100dvh;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}

.sg-page.is-playing .sg-header {
  margin-bottom: 0;
}

.sg-page.is-playing .sg-header h1 {
  font-size: 1.3rem;
}

.sg-page.is-playing .sg-header p {
  display: none;
}

.sg-page.is-playing .sg-round-bar {
  margin-bottom: 0;
}

.sg-muted {
  color: var(--sg-muted);
  font-weight: 400;
}

.sg-loading {
  text-align: center;
  color: var(--sg-muted);
  padding: 80px 0;
}

/* ---------------------------------------------------------------- header */

.sg-header {
  position: relative;
  margin-bottom: 24px;
}

/*
 * The wordmark is Fraunces wherever it appears, not only on the start screen.
 * It is the quest's name, and having it set in the system sans on the game and
 * tutorial screens made those pages look like a different site to the one you
 * came in through.
 */
.sg-header h1 {
  margin: 0;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  letter-spacing: 0.02em;
}

/* A link back to the start, but it should still read as the masthead. */
.sg-brand {
  color: inherit;
  text-decoration: none;
}

.sg-brand:hover {
  color: var(--sg-accent);
}

.sg-header p {
  margin: 4px 0 0;
  color: var(--sg-muted);
}

.sg-home {
  position: absolute;
  top: 4px;
  right: 0;
  color: var(--sg-muted);
  text-decoration: none;
  font-size: 0.85rem;
}

.sg-home:hover {
  color: var(--sg-accent);
}

/* ------------------------------------------------------------ start screen
 *
 * The start screen has no cards, no panels and no borders. Everything sits
 * directly on the live sky behind it, held together by type, spacing and one
 * hairline — which is the whole point: a page about looking at the night sky
 * should not open with a stack of rounded rectangles.
 *
 * It is also pinned to the viewport and never scrolls, matching the in-game
 * screen below and the rest of the site. Every size here is a clamp against
 * viewport units, because the layout has to survive a short laptop window as
 * well as a phone.
 */

.sg-page.is-start {
  max-width: none;
  height: 100vh;
  height: 100dvh;
  padding: 0;
  overflow: hidden;
  display: grid;
  grid-template-rows: 1fr auto;
  align-items: center;
  justify-items: center;
}

/* The real sky, drawn by SkyBackdrop. Behind everything and deaf to the mouse. */
.sg-backdrop {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.95;
}

/*
 * The layer the backdrop's hovercard lives on: over the sky, under nothing that
 * matters, and deaf to the pointer so it cannot chase itself around the screen.
 */
.sg-backdrop-hover {
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

/*
 * Lift the page's own content above the canvas. The overlay is excluded by name
 * and not by accident: this selector outranks `.sg-how-veil`'s own rule, so
 * without the second `:not` it overwrote `position: fixed` with `relative` and
 * the rules opened as a third row at the bottom of the grid instead of as a
 * modal over the top.
 */
.sg-page.is-start > *:not(.sg-backdrop):not(.sg-how-veil):not(.sg-backdrop-hover),
.sg-page.is-results > *:not(.sg-backdrop):not(.sg-backdrop-hover) {
  position: relative;
  z-index: 1;
}

/*
 * Dimmer behind the results, which are dense text and numbers rather than four
 * centred lines. It is there to say "still the same night", not to be read.
 */
.sg-page.is-results .sg-backdrop {
  opacity: 0.3;
}

.sg-start {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 20px;
}

/* --------------------------------------------------------------- the mark */

.sg-page.is-start .sg-header {
  margin-bottom: clamp(24px, 6vh, 56px);
  text-align: center;
}

/*
 * Fraunces, wide and centred. The letterspacing is doing as much work as the
 * face: a wordmark set at normal tracking reads as a heading, and set open like
 * this it reads as a title plate.
 */
.sg-page.is-start .sg-header h1 {
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: clamp(2.4rem, 9vw, 5.2rem);
  letter-spacing: 0.06em;
  line-height: 1.05;
  margin: 0;
}

.sg-page.is-start .sg-header p {
  margin: clamp(10px, 2vh, 18px) auto 0;
  max-width: 34rem;
  font-size: clamp(0.9rem, 1.6vw, 1.02rem);
  line-height: 1.6;
}

/* Out of the centred block entirely, into the corner of the window. */
.sg-page.is-start .sg-home {
  position: fixed;
  top: 18px;
  left: 20px;
  right: auto;
  z-index: 2;
}

/* ------------------------------------------------------------ the choices */

/*
 * Two halves of one line, divided by a single hairline. No fill and no border:
 * the hit area is generous, and hover lights the words rather than drawing a box
 * around them.
 */
.sg-tiers {
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
  align-items: stretch;
  gap: 0;
  width: min(760px, 100%);
}

.sg-tiers::before {
  content: "";
  grid-column: 2;
  grid-row: 1;
  background: linear-gradient(
    to bottom,
    transparent,
    rgba(190, 210, 245, 0.28) 22%,
    rgba(190, 210, 245, 0.28) 78%,
    transparent
  );
}

.sg-tier {
  grid-row: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.sg-tier:first-of-type {
  grid-column: 1;
}

.sg-tier:last-of-type {
  grid-column: 3;
}

.sg-tier-main {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: clamp(14px, 3vh, 26px) clamp(12px, 3vw, 30px);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  border-radius: 0;
  transition: color 0.18s;
}

.sg-tier-name {
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: clamp(1.7rem, 5vw, 2.9rem);
  letter-spacing: 0.02em;
  line-height: 1.1;
}

.sg-tier-note {
  color: var(--sg-muted);
  font-size: clamp(0.78rem, 1.4vw, 0.9rem);
  line-height: 1.5;
  max-width: 22rem;
}

.sg-tier-go {
  margin-top: 4px;
  color: var(--sg-accent);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(-3px);
  transition: opacity 0.18s, transform 0.18s;
}

/*
 * The glow is the hover state. It is a radial wash behind the words rather than
 * a border or a fill, so nothing on this page ever acquires an edge.
 */
.sg-tier-main:hover,
.sg-tier-main:focus-visible {
  outline: none;
  background: radial-gradient(
    ellipse at center,
    rgba(127, 178, 255, 0.16),
    rgba(127, 178, 255, 0) 68%
  );
}

.sg-tier-main:hover .sg-tier-name,
.sg-tier-main:focus-visible .sg-tier-name {
  color: #fff;
  text-shadow: 0 0 26px rgba(127, 178, 255, 0.65);
}

.sg-tier-main:hover .sg-tier-go,
.sg-tier-main:focus-visible .sg-tier-go {
  opacity: 1;
  transform: none;
}

/* Below this the hairline turns horizontal and the two choices stack. */
@media (max-width: 700px) {
  .sg-tiers {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1px auto;
  }

  .sg-tiers::before {
    grid-column: 1;
    grid-row: 2;
    background: linear-gradient(
      to right,
      transparent,
      rgba(190, 210, 245, 0.28) 22%,
      rgba(190, 210, 245, 0.28) 78%,
      transparent
    );
  }

  .sg-tier:first-of-type {
    grid-column: 1;
    grid-row: 1;
  }

  .sg-tier:last-of-type {
    grid-column: 1;
    grid-row: 3;
  }
}

/* ----------------------------------------------------- footer of the page */

.sg-start-foot {
  display: flex;
  align-items: center;
  gap: clamp(16px, 4vw, 34px);
  padding: 0 20px clamp(18px, 4vh, 34px);
  font-size: 0.9rem;
}

/*
 * Centred under the two choices, and a step up in size from the footer links
 * below it. Not a fourth item in that row: the rules and the settings are
 * reference you go and look something up in, and this is the way into the
 * quest for anyone the sky means nothing to yet.
 */
.sg-learn,
.sg-how-open {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 2px;
  background: none;
  border: none;
  border-bottom: 1px solid transparent;
  color: var(--sg-muted);
  font: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.sg-learn:hover,
.sg-learn:focus-visible,
.sg-how-open:hover,
.sg-how-open:focus-visible {
  outline: none;
  color: var(--sg-text);
  border-bottom-color: var(--sg-accent);
}

.sg-learn-mark {
  color: var(--sg-accent);
  font-size: 1rem;
  line-height: 1;
}

.sg-learn {
  margin-top: clamp(18px, 4vh, 40px);
  font-size: clamp(0.98rem, 1.7vw, 1.1rem);
  /*
   * Carries its underline all the time rather than finding one on hover, and
   * sits at full text colour rather than muted. Everything else on this page is
   * either enormous (the two choices) or plainly a footer link; without these
   * this one read as a caption under the buttons instead of as the third thing
   * you can do here.
   */
  color: var(--sg-text);
  border-bottom-color: rgba(127, 178, 255, 0.4);
  padding: 8px 4px;
}

.sg-learn:hover,
.sg-learn:focus-visible {
  border-bottom-color: var(--sg-accent);
  text-shadow: 0 0 18px rgba(127, 178, 255, 0.45);
}

.sg-learn .sg-learn-mark {
  font-size: 1.15em;
}


/* ------------------------------------------------------------- the rules */

/*
 * An overlay, not a disclosure box. The start screen is pinned to the viewport,
 * so there is nowhere for an expanding panel to go; this comes over the top and
 * scrolls inside itself when the window is short.
 */
.sg-how-veil {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  padding: 20px;
  background: rgba(3, 5, 11, 0.82);
  backdrop-filter: blur(3px);
}

.sg-how-sheet {
  position: relative;
  width: min(760px, 100%);
  max-height: min(80vh, 100%);
  overflow-y: auto;
  padding: clamp(20px, 4vw, 34px);
  background: rgba(6, 9, 18, 0.96);
  border-top: 1px solid rgba(190, 210, 245, 0.22);
  border-bottom: 1px solid rgba(190, 210, 245, 0.22);
}

.sg-how-sheet h2 {
  margin: 0 0 14px;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: 1.6rem;
  letter-spacing: 0.02em;
}

.sg-how-close {
  position: absolute;
  top: 10px;
  right: 14px;
  padding: 4px 8px;
  background: none;
  border: none;
  color: var(--sg-muted);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.sg-how-close:hover,
.sg-how-close:focus-visible {
  outline: none;
  color: var(--sg-text);
}

/* ---------------------------------------------------------------- settings */

/*
 * The way into settings on every screen but the start one, which has its own
 * link in the footer. Sits just left of "Side Quests" in the masthead corner.
 */
.sg-gear {
  position: absolute;
  top: -2px;
  right: 106px;
  padding: 2px 6px;
  background: none;
  border: none;
  /*
   * Brighter and larger than the muted link beside it. At the muted colour and
   * a 1rem glyph it was easy to miss entirely, which is a poor showing for the
   * only way into settings on four of the five screens.
   */
  color: rgba(214, 226, 248, 0.85);
  font-size: 1.45rem;
  line-height: 1.2;
  cursor: pointer;
  transition: color 0.15s, transform 0.15s;
}

.sg-gear:hover,
.sg-gear:focus-visible {
  outline: none;
  color: var(--sg-accent);
  transform: rotate(35deg);
}

/* The start screen offers it in the footer instead, spelled out. */
.sg-page.is-start .sg-gear {
  display: none;
}

/*
 * A definition list: name on the left, controls on the right, one hairline per
 * row. The rows carried a sentence of explanation each until it was clear they
 * were explaining things the labels already said.
 */
.sg-settings {
  margin: 0;
  padding: 0;
}

.sg-setting {
  display: grid;
  grid-template-columns: 150px 1fr;
  align-items: start;
  gap: 20px;
  padding: 18px 0;
  border-top: 1px solid var(--sg-line);
}

.sg-setting dt {
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: 0.98rem;
  letter-spacing: 0.01em;
  padding-top: 1px;
}

.sg-setting dd {
  margin: 0;
  min-width: 0;
}

/* Choices are underlined words, like the results screen's tier switch. */
.sg-setting-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
}

.sg-setting-choices button {
  padding: 2px 1px;
  background: none;
  border: none;
  border-bottom: 1px solid transparent;
  color: var(--sg-muted);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.sg-setting-choices button:hover,
.sg-setting-choices button:focus-visible {
  outline: none;
  color: var(--sg-text);
}

.sg-setting-choices button.is-current {
  color: var(--sg-text);
  border-bottom-color: var(--sg-accent);
}

/* ------------------------------------------------------------- the slider */

/*
 * Inset by half a label cell at each end, so the thumb travels between the label
 * centres rather than between the edges of the row. With four stops each label
 * owns a quarter of the width and sits in the middle of it — centres at 12.5%,
 * 37.5%, 62.5%, 87.5% — which is where the thumb now stops. The 12.5% is that
 * stop count, and a test pins the count so this cannot quietly drift out of
 * step.
 */
.sg-setting-slider input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 75%;
  height: 18px;
  margin: 0 12.5%;
  background: none;
  cursor: pointer;
}

.sg-setting-slider input[type="range"]::-webkit-slider-runnable-track {
  height: 1px;
  background: var(--sg-line);
}

.sg-setting-slider input[type="range"]::-moz-range-track {
  height: 1px;
  background: var(--sg-line);
}

/* A star, near enough: a small round mark travelling a hairline. */
.sg-setting-slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border: none;
  border-radius: 50%;
  background: var(--sg-accent);
  box-shadow: 0 0 10px rgba(127, 178, 255, 0.7);
}

.sg-setting-slider input[type="range"]::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border: none;
  border-radius: 50%;
  background: var(--sg-accent);
  box-shadow: 0 0 10px rgba(127, 178, 255, 0.7);
}

.sg-setting-slider input[type="range"]:focus-visible {
  outline: none;
}

.sg-setting-slider input[type="range"]:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px rgba(127, 178, 255, 0.35);
}

/*
 * Every stop named under the track, so the slider says what it is choosing
 * between rather than making you drag it to find out. Justified to the ends so
 * each label sits under its own position on the track.
 */
/*
 * Every stop named under the track, so the slider says what it is choosing
 * between rather than making you drag it to find out. Equal cells, every label
 * centred in its own — the ends used to be flush left and right, which left the
 * outer gaps visibly wider than the inner ones and the labels off their stops.
 */
.sg-setting-ticks {
  display: flex;
  margin-top: 4px;
  font-size: 0.72rem;
  color: var(--sg-muted);
}

.sg-setting-ticks span {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  transition: color 0.15s;
}

.sg-setting-ticks .is-current {
  color: var(--sg-text);
}

/* The one line of prose in the menu, under the slider it belongs to. */
.sg-setting-note {
  margin: 12px 0 0;
  color: var(--sg-muted);
  font-size: 0.76rem;
  line-height: 1.5;
}

/*
 * Always present, dimmed until there is something to reset. A control that only
 * appears once you have changed something is a control nobody knows is there,
 * and its absence reads as the menu having lost a button.
 */
.sg-setting-reset {
  padding: 4px 1px;
  background: none;
  border: none;
  border-bottom: 1px solid transparent;
  color: var(--sg-muted);
  font: inherit;
  font-size: 0.82rem;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.sg-setting-reset:hover:not(:disabled),
.sg-setting-reset:focus-visible:not(:disabled) {
  outline: none;
  color: var(--sg-text);
  border-bottom-color: var(--sg-accent);
}

.sg-setting-reset:disabled {
  opacity: 0.4;
  cursor: default;
}

/*
 * Two columns of a comfortable measure, rather than one column of a comfortable
 * measure and a lot of empty box beside it. `auto-fit` drops to one column on a
 * narrow screen without needing a breakpoint.
 */
.sg-how-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 4px 32px;
  align-items: start;
  margin-top: 6px;
  margin-bottom: 20px;
}

.sg-how-sheet p {
  margin: 8px 0 0;
  color: var(--sg-muted);
  font-size: 0.9rem;
}

.sg-how-more {
  margin-top: 20px;
  padding-top: 12px;
  border-top: 1px solid rgba(190, 210, 245, 0.18);
}

.sg-how-link {
  padding: 0;
  background: none;
  border: none;
  font: inherit;
  color: var(--sg-accent);
  text-decoration: underline;
  cursor: pointer;
}

.sg-how-sheet strong {
  color: var(--sg-text);
}

/*
 * The tutorial fills the viewport like a round does: sky on the left, text on
 * the right. The lessons are animations, so they need the room.
 */
.sg-tutorial {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: 14px;
}

.sg-tutorial-stage {
  position: relative;
  min-height: 0;
}

/*
 * Top centre, clear of the horizon. Down at the bottom it sat on top of the
 * altitude measurement, which is the one thing lesson three needs you to read.
 */
.sg-tutorial-caption {
  position: absolute;
  left: 0;
  right: 0;
  top: 18px;
  margin: 0;
  text-align: center;
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  color: rgba(226, 238, 255, 0.92);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.9);
  pointer-events: none;
}

.sg-tutorial-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  justify-content: center;
  gap: 8px;
}

.sg-tutorial-dots button {
  width: 9px;
  height: 9px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(150, 185, 235, 0.55);
  background: transparent;
  cursor: pointer;
}

.sg-tutorial-dots button.is-current {
  background: var(--sg-accent);
  border-color: var(--sg-accent);
}

/*
 * The inset map, top right: clear of the caption running across the top centre,
 * of the lesson dots along the bottom, and of the horizon and its guides, which
 * all sit low in the frame. Small enough to read as an aside, since it is
 * context for the sky rather than the thing being taught.
 */
.sg-minimap {
  position: absolute;
  right: 14px;
  top: 14px;
  width: 300px;
  margin: 0;
  padding: 8px 8px 6px;
  background: rgba(6, 10, 20, 0.72);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  pointer-events: none;
}

.sg-minimap svg {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0;
}

.sg-minimap figcaption {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 6px;
  padding: 0 2px;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--sg-muted);
}

/* Same blue-grey as the coordinates beside it: one caption, not two readouts. */
.sg-minimap-clock {
  white-space: nowrap;
}

.sg-minimap-sea {
  fill: #0a1226;
}

.sg-minimap-land {
  fill: #23314f;
}

/* The Earth's shadow. Translucent, so the coastlines stay readable through it. */
.sg-minimap-night {
  fill: rgba(2, 4, 10, 0.62);
}

.sg-minimap-parallel {
  stroke: rgba(255, 207, 92, 0.5);
  stroke-width: 3;
  stroke-dasharray: 14 12;
}

.sg-minimap-pin circle {
  fill: var(--sg-guess);
}

.sg-minimap-halo {
  fill: rgba(255, 207, 92, 0.22);
}

.sg-tutorial-text {
  background: var(--sg-panel);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  padding: 20px;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.sg-tutorial-step {
  margin: 0;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--sg-muted);
}

.sg-tutorial-text h2 {
  margin: 6px 0 10px;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: 1.45rem;
  letter-spacing: 0.01em;
  line-height: 1.25;
}

.sg-tutorial-blurb {
  margin: 0;
  color: var(--sg-muted);
  font-size: 0.94rem;
}

/* A lesson may run to several paragraphs; the first keeps the heading's spacing. */
.sg-tutorial-blurb + .sg-tutorial-blurb {
  margin-top: 10px;
}

.sg-tutorial-nav {
  display: flex;
  gap: 8px;
  margin-top: auto;
  padding-top: 18px;
}

.sg-tutorial-nav button {
  flex: 1;
}

.sg-tutorial-skip {
  margin-top: 8px;
  background: none;
  border: none;
  color: var(--sg-muted);
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  text-decoration: underline;
}

.sg-tutorial-skip:hover {
  color: var(--sg-accent);
}

@media (max-width: 820px) {
  /*
   * The animation is the lesson, so it gets a guaranteed share of the height
   * rather than whatever the prose leaves over.
   *
   * `1fr auto` gave the text row its full content height and the stage the
   * remainder, which on a phone is a lesson about the sky playing in a 185px
   * letterbox with a 300px map sitting on top of it. Reversing that — a floor
   * for the stage, the prose scrolling in what is left — is the whole fix, and
   * most of the overlaps below stop happening once the stage is a real size.
   */
  .sg-tutorial {
    grid-template-columns: 1fr;
    /*
     * A little more than the map's band costs, so moving it out of the sky is
     * not paid for by the sky.
     */
    grid-template-rows: minmax(300px, 48vh) minmax(0, 1fr);
  }

  .sg-tutorial-text {
    padding: 16px 14px;
  }

  /*
   * Back and Next stay put while the lesson scrolls past them.
   *
   * Once the prose is in a scrolling column, the buttons that move between
   * lessons are wherever the reader last left the scroll — which on a short
   * screen is reliably off the bottom, with nothing to say they exist. Sticky
   * keeps them where a footer would be. It needs an opaque fill of its own,
   * since the panel's is translucent and the text would read straight through.
   */
  .sg-tutorial-nav {
    position: sticky;
    bottom: 0;
    z-index: 1;
    padding-bottom: 10px;
    background: #080b14;
  }
}

/*
 * The narrowest phones. The inset map at 190px is nearly two thirds of a 300px
 * stage, which leaves the caption a 82px lane and a word per line, so both come
 * down together and the lane lands back at the ~120px the wider phones give it.
 */
/*
 * The tutorial with a phone on its side. It is still two columns here — the
 * stage is wide enough — but only 340px tall, so the prose scrolls and its
 * buttons need pinning, same as in portrait.
 */
@media (max-height: 520px) {
  .sg-tutorial-text {
    padding: 14px;
  }

  .sg-tutorial-nav {
    position: sticky;
    bottom: 0;
    z-index: 1;
    padding-top: 12px;
    padding-bottom: 8px;
    background: #080b14;
  }
}

/* ================================ the inset map, off the sky and beside it ==
 *
 * On a desktop stage the map is a 300px inset in the corner of a 1000px sky:
 * a quarter of the width, over a part of the frame the lessons keep clear, and
 * translucent so the sky reads faintly through it as depth.
 *
 * None of that survives a phone. At 190px on a 370px stage it is half the
 * width, there is no part of the frame the lessons keep clear because there is
 * barely any frame, and the translucency stops reading as depth and starts
 * reading as two pictures printed on top of each other — constellation lines
 * and star names legible *through* the map is what "the map overlaps the star
 * display" actually looks like.
 *
 * So at this size they stop sharing space. The stage reserves a band along its
 * bottom edge that the sky canvas does not paint into, and the map lives there:
 * laid out sideways, since a band is wide and short and the map's natural
 * portrait stack is neither. The sky above it is then whole — no panel over it,
 * nothing showing through, and every guide label free to use the full width.
 *
 * Keyed on either axis being small, because the map crowds the sky on a phone
 * held upright and on the same phone turned over.
 */
@media (max-width: 820px), (max-height: 520px) {
  /* The band. Padding rather than a shorter stage, so the border still frames
     the whole thing and the dots keep somewhere to sit. */
  .sg-tutorial-stage {
    padding-bottom: 96px;
  }

  /* The sky stops at the band rather than running under it. */
  .sg-tutorial-stage .sg-sky {
    bottom: 96px;
  }

  .sg-minimap {
    top: auto;
    right: 10px;
    bottom: 6px;
    left: 10px;
    width: auto;
    display: flex;
    align-items: center;
    /* Centred as a pair, so the band looks the same on a 300px phone and on the
       460px one it becomes when the phone is turned over. */
    justify-content: center;
    gap: 14px;
    padding: 5px 6px;
    /* Opaque: it is beside the sky now, not over it. */
    background: #070b14;
  }

  .sg-minimap svg {
    width: 150px;
    flex: none;
  }

  .sg-minimap figcaption {
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    margin-top: 0;
    font-size: 0.7rem;
  }

  /*
   * Just above the band, still over the sky — they sit low in the frame where
   * the ground is, which is empty in every lesson.
   */
  .sg-tutorial-dots {
    bottom: 106px;
  }

  /* Centred across the full width again: there is nothing up there to dodge. */
  .sg-tutorial-caption {
    top: 12px;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 0.92rem;
    line-height: 1.35;
  }
}

/* ------------------------------------------------------------- round bar */

.sg-round-bar {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  background: var(--sg-panel);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  padding: 12px 16px;
  margin-bottom: 12px;
}

.sg-round-count {
  font-weight: 600;
}

.sg-given {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 0.9rem;
}

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

.sg-unknown {
  color: var(--sg-muted);
  font-style: italic;
  font-weight: 400;
}

.sg-score-so-far {
  margin-left: auto;
  color: var(--sg-accent);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* --------------------------------------------------------------- the sky */

/*
 * The round is a fixed-height stage, not a scrolling document. The sky fills
 * whatever is left after the header and the round bar, and the guess panel
 * floats over it, so the page never grows a scrollbar and the sky always gets
 * the room.
 */
.sg-round {
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: 100%;
  min-height: 0;
}

.sg-stage {
  position: relative;
  flex: 1;
  min-height: 0;
  border-radius: 0;
  overflow: hidden;
  border: 1px solid var(--sg-line);
  background: #04060d;
}

.sg-sky {
  position: absolute;
  inset: 0;
}

.sg-sky-canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: none;
}

.sg-sky-canvas.is-fixed,
.sg-sky-canvas.is-fixed:active {
  cursor: default;
}

.sg-sky-canvas:active {
  cursor: grabbing;
}

.sg-sky-hint {
  position: absolute;
  left: 12px;
  bottom: 10px;
  font-size: 0.75rem;
  color: rgba(200, 215, 245, 0.45);
  pointer-events: none;
}

/*
 * Out from under the compass rose, which claims the same corner on exactly the
 * same coordinates. Both are shown at once for the whole of an Easy round before
 * the guess is locked — the compass needs `revealed || assisted`, the hint needs
 * `!revealed` — so the two sat on top of each other and rendered as one line of
 * interleaved letters.
 *
 * Keyed off the rose actually being in the DOM rather than off the tier, because
 * the hint should stay in the corner in Hard, where there is no rose to clear.
 * The rose is 86px wide from `left: 12px`, so this leaves a 12px gutter and puts
 * the hint on the same baseline as the "facing" label beside it.
 */
.sg-compass ~ .sg-sky-hint {
  left: 110px;
}

.sg-sky-keys {
  position: absolute;
  left: 12px;
  top: 12px;
  display: flex;
  gap: 4px;
}

.sg-sky-keys button {
  width: 30px;
  height: 30px;
  border-radius: 0;
  border: 1px solid rgba(140, 170, 220, 0.25);
  background: rgba(10, 16, 30, 0.6);
  color: var(--sg-text);
  cursor: pointer;
  font-size: 0.9rem;
}

.sg-sky-keys button:hover {
  border-color: var(--sg-accent);
}

/* ------------------------------------------------------- the label toggles */

.sg-layers {
  position: absolute;
  left: 12px;
  top: 50px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  z-index: 3;
}

.sg-layers button {
  display: flex;
  align-items: center;
  gap: 7px;
  border: 1px solid rgba(140, 170, 220, 0.25);
  /*
   * Near-opaque, because these float over the sky canvas and the canvas draws
   * star names. At 0.68 a label behind a chip read straight through it —
   * "Aldebaran" printed across the word "Stars".
   */
  background: rgba(10, 16, 30, 0.97);
  color: var(--sg-muted);
  font: inherit;
  font-size: 0.75rem;
  padding: 4px 10px 4px 6px;
  border-radius: 0;
  cursor: pointer;
  text-align: left;
}

/* A real checkbox, because a button that only changes shade does not read as
   on or off at a glance. */
.sg-layer-box {
  width: 13px;
  height: 13px;
  flex: none;
  border-radius: 0;
  border: 1px solid rgba(150, 180, 225, 0.5);
  display: grid;
  place-items: center;
  font-size: 9px;
  line-height: 1;
  color: #06101f;
}

.sg-layers button.is-on .sg-layer-box {
  background: var(--sg-accent);
  border-color: var(--sg-accent);
}

.sg-layers button.is-on {
  color: var(--sg-text);
  border-color: rgba(127, 178, 255, 0.55);
  background: rgba(28, 44, 76, 0.97);
}

.sg-layers button:hover {
  border-color: var(--sg-accent);
}

/* ---------------------------------------------------------- the compass */

/*
 * Bottom left, not bottom right: the guess panel is docked to the right edge and
 * would sit on top of it. The drag hint gives up that corner once the round is
 * revealed, by which point nobody needs telling to drag.
 */
.sg-compass {
  position: absolute;
  left: 12px;
  bottom: 10px;
  width: 86px;
  text-align: center;
  pointer-events: none;
  z-index: 3;
}

.sg-compass svg {
  display: block;
  width: 86px;
  height: 86px;
  overflow: visible;
}

.sg-compass-ring {
  fill: rgba(8, 12, 22, 0.55);
  stroke: rgba(140, 175, 225, 0.3);
  stroke-width: 1.5;
}

.sg-compass-major,
.sg-compass-minor {
  text-anchor: middle;
  dominant-baseline: central;
  font-family: system-ui, sans-serif;
}

.sg-compass-major {
  fill: rgba(214, 230, 255, 0.95);
  font-size: 15px;
  font-weight: 700;
}

.sg-compass-minor {
  fill: rgba(160, 188, 228, 0.6);
  font-size: 9px;
}

.sg-compass-needle {
  fill: var(--sg-accent);
}

.sg-compass-label {
  display: block;
  margin-top: 2px;
  font-size: 0.7rem;
  color: rgba(190, 210, 240, 0.72);
}

/* ------------------------------------------------------ the star hovercard */

.sg-star-card {
  position: absolute;
  transform: translate(14px, -50%);
  min-width: 190px;
  max-width: 250px;
  background: rgba(9, 13, 24, 0.94);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  padding: 9px 11px;
  pointer-events: none;
  z-index: 5;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5);
}

.sg-star-name {
  margin: 0;
  font-weight: 700;
  font-size: 0.92rem;
}

.sg-star-bayer {
  margin: 1px 0 0;
  font-size: 0.78rem;
  color: var(--sg-muted);
}

/* Roomier than the star card, since this one is prose rather than a table. */
.sg-lore-card {
  min-width: 250px;
  max-width: 310px;
}

.sg-lore {
  margin: 8px 0 0;
  font-size: 0.82rem;
  line-height: 1.45;
  color: rgba(214, 226, 246, 0.88);
}

.sg-lore-star {
  margin: 8px 0 0;
  padding-top: 7px;
  border-top: 1px solid var(--sg-line);
  font-size: 0.78rem;
}

.sg-star-card dl {
  margin: 7px 0 0;
  display: grid;
  gap: 3px;
  font-size: 0.78rem;
}

.sg-star-card dl > div {
  display: flex;
  justify-content: space-between;
  gap: 10px;
}

.sg-star-card dt {
  color: var(--sg-muted);
  flex: none;
}

.sg-star-card dd {
  margin: 0;
  text-align: right;
}

/* --------------------------------------------------------- the world map */

.sg-map {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0;
  border: 1px solid var(--sg-line);
  background: var(--sg-panel);
  /* The map handles its own drag to pan, so the browser must not also scroll. */
  touch-action: none;
}

.sg-map-active {
  cursor: crosshair;
}

.sg-map-sea {
  fill: #0a1226;
}

.sg-map-land {
  fill: #1d2b47;
  stroke: #33456d;
  stroke-width: 0.8;
}

.sg-map-link {
  stroke: rgba(255, 255, 255, 0.35);
}

.sg-pin circle {
  fill: none;
}

.sg-pin .sg-pin-core {
  stroke: none;
}

.sg-pin-guess circle {
  stroke: var(--sg-guess);
}

.sg-pin-guess .sg-pin-core {
  fill: var(--sg-guess);
}

.sg-pin-actual circle {
  stroke: var(--sg-actual);
}

.sg-pin-actual .sg-pin-core {
  fill: var(--sg-actual);
}

.sg-pin-label {
  fill: var(--sg-actual);
  font-weight: 700;
  text-anchor: middle;
  stroke: none;
}

/* ------------------------------------------------------- the guess panel */

/*
 * Floats over the sky in the top right and can be dragged anywhere by its
 * header. Docking it to the full right edge cost the sky a third of the frame
 * and still left the map small, which is the wrong trade in a game about
 * looking at the sky.
 */
.sg-panel {
  position: absolute;
  top: 12px;
  right: 12px;
  max-width: calc(100vw - 24px);
  background: rgba(8, 12, 22, 0.94);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55);
  z-index: 4;
  overflow: hidden;
}

.sg-panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  cursor: grab;
  user-select: none;
  background: rgba(18, 26, 46, 0.9);
  border-bottom: 1px solid var(--sg-line);
  touch-action: none;
}

.sg-panel-head:active {
  cursor: grabbing;
}

.sg-panel-grip {
  color: var(--sg-muted);
  font-size: 0.9rem;
  letter-spacing: -1px;
}

.sg-panel-title {
  flex: 1;
  font-size: 0.85rem;
  font-weight: 600;
}

.sg-panel-close {
  width: 24px;
  height: 24px;
  border-radius: 0;
  border: 1px solid var(--sg-line);
  background: transparent;
  color: var(--sg-text);
  font: inherit;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
}

.sg-panel-close:hover {
  border-color: var(--sg-accent);
}

/* Width is set inline, so the corner handle can drive it. */

.sg-panel-resize {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 18px;
  height: 18px;
  cursor: nesw-resize;
  touch-action: none;
  background: linear-gradient(
    45deg,
    transparent 0 46%,
    var(--sg-line) 46% 54%,
    transparent 54% 70%,
    var(--sg-line) 70% 78%,
    transparent 78%
  );
}

.sg-panel-resize:hover {
  background: linear-gradient(
    45deg,
    transparent 0 46%,
    var(--sg-accent) 46% 54%,
    transparent 54% 70%,
    var(--sg-accent) 70% 78%,
    transparent 78%
  );
}

.sg-panel-inner {
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sg-panel-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sg-guess-readout {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sg-guess-coords {
  margin: 0;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.sg-guess-time {
  margin: 0;
  font-size: 0.84rem;
}

.sg-guess-prompt {
  margin: 0;
  font-size: 0.9rem;
  color: var(--sg-muted);
}

.sg-reveal-note {
  margin: 0;
  font-size: 0.78rem;
  color: var(--sg-muted);
}

.sg-date-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--sg-muted);
}

.sg-date-field input {
  background: var(--sg-panel-2);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  color: var(--sg-text);
  padding: 8px 10px;
  font: inherit;
  color-scheme: dark;
}

.sg-primary,
.sg-secondary {
  border-radius: 0;
  padding: 10px 14px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid transparent;
}

.sg-primary {
  background: var(--sg-accent);
  color: #06101f;
}

/*
 * The last lesson's button says the quest's name, so it is set in its face.
 * Fraunces runs wider than the system sans at the same size, which wrapped the
 * two words onto separate lines and left this button taller than the Back
 * beside it, so it is held on one line and sized down to fit.
 */
.sg-primary.sg-wordmark {
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-size: 0.92rem;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.sg-primary:disabled {
  background: var(--sg-panel-2);
  color: var(--sg-muted);
  cursor: not-allowed;
}

.sg-secondary {
  background: transparent;
  border-color: var(--sg-line);
  color: var(--sg-text);
}

.sg-secondary:hover {
  border-color: var(--sg-accent);
}

/*
 * The panel docked: a sheet along the bottom of the stage rather than a window
 * floating over it. `is-docked` is put on by the component at the breakpoint in
 * `viewport.mjs`, which also stops it writing an inline width — the inline width
 * is why the old `width: calc(100vw - 24px)` rescue here never fired.
 *
 * Sized by its content up to a ceiling, not stretched to the ceiling: with a pin
 * still to drop, the sheet is a map and a button and the rest of the stage stays
 * sky, which is the thing being read.
 */
.sg-panel.is-docked {
  top: auto;
  right: 0;
  bottom: 0;
  left: 0;
  width: auto;
  max-width: none;
  max-height: 74%;
  display: flex;
  flex-direction: column;
  /*
   * Opaque, where the floating panel is 94%. Against a whole edge of the stage
   * that last 6% stops reading as depth and starts reading as a fault: the
   * compass and the drag hint sit in the corner the sheet now covers, and they
   * showed through it as ghost text under the map.
   */
  background: #080c16;
  border-right: none;
  border-bottom: none;
  border-left: none;
  box-shadow: 0 -12px 34px rgba(0, 0, 0, 0.6);
}

.sg-panel.is-docked .sg-panel-head {
  flex: none;
  cursor: default;
}

/*
 * The sheet scrolls inside itself, and only inside itself. Without the
 * containment a flick that runs past the end of the result rows carries on into
 * the page behind, which on a screen pinned to the viewport means a rubber-band
 * over a layout that is not supposed to move.
 */
.sg-panel.is-docked .sg-panel-inner {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/*
 * A phone on its side. A bottom sheet here would leave a 60px strip of sky, so
 * the panel stands along the right-hand edge instead and takes the full height —
 * which is also the layout that puts the submit button back on the screen, the
 * bug that made this breakpoint necessary in the first place.
 */
@media (min-width: 560px) and (max-height: 520px) {
  .sg-panel.is-docked {
    top: 0;
    bottom: 0;
    left: auto;
    width: min(46%, 400px);
    max-height: none;
    border-top: none;
    border-left: 1px solid var(--sg-line);
    box-shadow: -12px 0 34px rgba(0, 0, 0, 0.6);
  }

  .sg-panel.is-docked.is-collapsed {
    bottom: auto;
    width: 200px;
  }
}

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

.sg-result-score {
  margin: 0;
  font-size: 1.6rem;
  font-variant-numeric: tabular-nums;
}

.sg-result-score strong {
  color: var(--sg-accent);
}

.sg-result-rows {
  margin: 0;
  display: grid;
  gap: 6px;
  font-size: 0.88rem;
}

.sg-result-rows > div {
  display: flex;
  justify-content: space-between;
  gap: 10px;
}

.sg-result-rows dt {
  color: var(--sg-muted);
}

.sg-result-rows dd {
  margin: 0;
  text-align: right;
}

.sg-result {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* ------------------------------------------------------------- summary */

/*
 * The results screen carries no boxes either. The summary and the board are
 * separated by the single hairline between them (see `.sg-results > * + *`) and
 * by the space around them, not by a border drawn around each.
 */
.sg-summary {
  padding: 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.sg-summary-head {
  text-align: center;
}

.sg-summary-tier {
  margin: 0;
  color: var(--sg-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.75rem;
}

/* The score is the headline of the page, so it is set like one. */
.sg-summary-head h2 {
  margin: 4px 0 0;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: clamp(2.8rem, 6vw, 4rem);
  letter-spacing: 0.01em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: var(--sg-text);
}

.sg-summary-head h2 .sg-muted {
  font-size: 1.1rem;
}

.sg-summary-rounds {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
}

/* Rows separated by a rule rather than by three filled bars. */
.sg-summary-rounds li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 2px;
  border-bottom: 1px solid var(--sg-line);
  font-size: 0.88rem;
}

.sg-summary-rounds li:last-child {
  border-bottom: none;
}

.sg-summary-index {
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--sg-line);
  color: var(--sg-muted);
  display: grid;
  place-items: center;
  font-size: 0.75rem;
  font-weight: 700;
}

.sg-summary-detail {
  flex: 1;
}

.sg-summary-points {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--sg-accent);
}

.sg-submit {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/*
 * Stacked on the leaderboard, where the column is too narrow to hold the field
 * and the button side by side without clipping the placeholder.
 */
.sg-board .sg-submit {
  flex-direction: column;
  margin-bottom: 14px;
}

.sg-board .sg-submit input {
  min-width: 0;
}

.sg-submit input {
  flex: 1;
  min-width: 180px;
  background: var(--sg-panel-2);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  color: var(--sg-text);
  padding: 10px 12px;
  font: inherit;
}

.sg-submitted {
  margin: 0;
  color: var(--sg-actual);
  font-size: 0.9rem;
}

/* The name the score went up under, picked out of the sentence around it. */
.sg-submitted strong {
  color: var(--sg-text);
}

/*
 * A text button, in the muted colour of the prose around it rather than the
 * green of the line it sits on: the sentence is the confirmation, and this is a
 * way to correct it, not a second thing to celebrate.
 */
.sg-name-change {
  padding: 2px 1px;
  background: none;
  border: none;
  border-bottom: 1px solid rgba(190, 210, 245, 0.35);
  color: var(--sg-muted);
  font: inherit;
  font-size: 0.82rem;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.sg-name-change:hover,
.sg-name-change:focus-visible {
  outline: none;
  color: var(--sg-text);
  border-bottom-color: var(--sg-accent);
}

/*
 * What the change actually did, on its own line. Changing the name does not move
 * the score already on the board, and this is where that is said — so it has to
 * be plainly readable rather than a footnote.
 */
.sg-name-note {
  display: block;
  flex-basis: 100%;
  margin: 8px 0 0;
  color: var(--sg-muted);
  font-size: 0.8rem;
  line-height: 1.45;
}

.sg-name-note strong {
  color: var(--sg-text);
}

.sg-error {
  margin: 0;
  flex-basis: 100%;
  color: #ff9d9d;
  font-size: 0.85rem;
}

/* ---------------------------------------------------------- leaderboard */

.sg-board {
  margin-top: 22px;
  padding: 16px 20px;
}

.sg-board-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.sg-board-head h2 {
  margin: 0;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: 0.01em;
}

/* Two words and an underline, rather than a segmented control on a grey tray. */
.sg-tier-toggle {
  display: flex;
  gap: 16px;
}

.sg-tier-toggle button {
  border: none;
  border-bottom: 1px solid transparent;
  background: transparent;
  color: var(--sg-muted);
  font: inherit;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 1px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.sg-tier-toggle button:hover,
.sg-tier-toggle button:focus-visible {
  outline: none;
  color: var(--sg-text);
}

.sg-tier-toggle button.is-active {
  color: var(--sg-text);
  border-bottom-color: var(--sg-accent);
}

.sg-board-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 4px;
}

.sg-board-list li {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 2px;
  border-bottom: 1px solid var(--sg-line);
  font-size: 0.9rem;
}

.sg-board-list li:last-child {
  border-bottom: none;
}

.sg-board-rank {
  width: 20px;
  color: var(--sg-muted);
  font-variant-numeric: tabular-nums;
}

.sg-board-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sg-board-score {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--sg-accent);
}

/* ------------------------------------------------------- brightness tuner */

/*
 * A development overlay, shown only when the URL asks for it (?brightness).
 * Bottom left: the layer toggles and compass live on the right, and the guess
 * panel opens top right.
 */
.sg-tuner {
  position: absolute;
  left: 14px;
  bottom: 14px;
  width: 210px;
  padding: 10px 12px 8px;
  background: rgba(6, 10, 20, 0.88);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  z-index: 5;
}

.sg-tuner-head {
  margin: 0 0 8px;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--sg-muted);
}

.sg-tuner-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.sg-tuner-list button {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 4px 7px;
  background: none;
  border: 1px solid transparent;
  border-radius: 0;
  color: var(--sg-text);
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
}

.sg-tuner-list button:hover {
  border-color: var(--sg-line);
}

.sg-tuner-list button.is-current {
  background: rgba(127, 178, 255, 0.16);
  border-color: var(--sg-accent);
}

.sg-tuner-key {
  display: inline-block;
  min-width: 15px;
  padding: 0 3px;
  border-radius: 0;
  background: rgba(255, 255, 255, 0.08);
  color: var(--sg-muted);
  font-size: 0.7rem;
  text-align: center;
}

.sg-tuner-note {
  margin: 8px 0 0;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--sg-muted);
}

.sg-tuner-numbers {
  margin: 5px 0 0;
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  color: rgba(138, 153, 184, 0.7);
}

/* ---------------------------------------------------------- on this night */

/*
 * The reveal for a round set at a real historical moment. It sits at the top of
 * the result panel, above the score: the score is the same arithmetic as any
 * other round, and this is the thing worth reading first.
 */
.sg-event {
  margin: 0 0 14px;
  padding: 12px 14px;
  background: linear-gradient(120deg, rgba(24, 40, 78, 0.85), rgba(14, 20, 38, 0.85));
  border: 1px solid rgba(127, 178, 255, 0.35);
  border-radius: 0;
}

.sg-event-lead {
  margin: 0;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--sg-muted);
}

.sg-event-name {
  margin: 4px 0 0;
  font-size: 1.02rem;
  font-weight: 600;
  line-height: 1.3;
}

.sg-event-where {
  margin: 5px 0 0;
  font-size: 0.82rem;
  color: var(--sg-accent);
}

.sg-event-note {
  margin: 8px 0 0;
  font-size: 0.84rem;
  line-height: 1.45;
  color: var(--sg-muted);
}

.sg-event-link {
  display: inline-block;
  margin: 8px 0 0;
  font-size: 0.8rem;
  color: var(--sg-accent);
  text-decoration: none;
  border-bottom: 1px solid rgba(127, 178, 255, 0.35);
}

.sg-event-link:hover,
.sg-event-link:focus-visible {
  border-bottom-color: var(--sg-accent);
}

/* --------------------------------------------------------------- explorer */

/*
 * The last tutorial page: a pin you can drag anywhere and two sliders. It sits
 * along the bottom, under the inset map in the top right and clear of the lesson
 * dots, which the explorer page keeps so you can still page back.
 */
.sg-explorer {
  position: absolute;
  left: 14px;
  bottom: 44px;
  width: 300px;
  padding: 12px 14px 10px;
  background: rgba(6, 10, 20, 0.86);
  border: 1px solid var(--sg-line);
  border-radius: 0;
}

.sg-explorer-row {
  display: grid;
  grid-template-columns: 38px 1fr 62px;
  align-items: center;
  gap: 8px;
}

.sg-explorer-row + .sg-explorer-row {
  margin-top: 8px;
}

.sg-explorer-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--sg-muted);
}

.sg-explorer-value {
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--sg-text);
}

.sg-explorer input[type='range'] {
  width: 100%;
  accent-color: var(--sg-accent);
}

.sg-explorer-daylight {
  margin: 9px 0 0;
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--sg-guess);
}

/* The map becomes a control on the explorer page rather than a readout. */
.sg-minimap.is-pickable {
  pointer-events: auto;
}

.sg-minimap.is-pickable svg {
  cursor: crosshair;
  touch-action: none;
}

/* ---------------------------------------------------------------- results */

/*
 * The results screen is pinned to the viewport like a round is, rather than
 * scrolling the page. Summary on the left, leaderboard on the right; each
 * column scrolls inside itself if it has to, so the page never does.
 */
.sg-page.is-results {
  max-width: none;
  height: 100vh;
  height: 100dvh;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}

.sg-page.is-results .sg-header {
  margin-bottom: 0;
}

.sg-page.is-results .sg-header h1 {
  font-size: 1.3rem;
}

.sg-page.is-results .sg-header p {
  display: none;
}

.sg-results {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 380px;
  gap: 14px;
}

.sg-results > * {
  min-height: 0;
  margin: 0;
  overflow-y: auto;
}

/*
 * One hairline between the summary and the board, in place of a border around
 * each. Same device as the two choices on the start screen, and the same reason:
 * a rule divides two things without making either of them a card.
 */
.sg-results > * + * {
  border-left: 1px solid var(--sg-line);
}

/*
 * The summary is a column whose map takes whatever height is left over, so a
 * short game and a tall window do not leave a hole and a small window does not
 * push the buttons off the bottom.
 */
.sg-page.is-results .sg-summary {
  display: flex;
  flex-direction: column;
}

.sg-page.is-results .sg-summary-map {
  flex: 1;
  min-height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.sg-page.is-results .sg-summary-map svg {
  max-height: 100%;
  width: auto;
  max-width: 100%;
}

/*
 * The share button sits under the score, which is what it copies, centred where
 * the moon row used to be. The moons moved onto the round rows: one beside each
 * distance, rather than a separate row restating all three.
 */
.sg-share-button {
  margin-top: 12px;
  padding: 9px 4px;
  border: none;
  border-bottom: 1px solid rgba(127, 178, 255, 0.45);
  border-radius: 0;
  background: none;
  color: var(--sg-accent);
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.sg-share-button:hover,
.sg-share-button:focus-visible {
  outline: none;
  color: var(--sg-text);
  border-bottom-color: var(--sg-accent);
}

.sg-summary-moon {
  font-size: 1.05rem;
  line-height: 1;
}

.sg-share-text {
  display: block;
  width: 100%;
  margin-top: 10px;
  padding: 8px 10px;
  background: var(--sg-panel-2);
  color: var(--sg-text);
  border: 1px solid var(--sg-line);
  border-radius: 0;
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  resize: none;
}

/*
 * One column, and the page scrolls.
 *
 * Pinning the results to the viewport works when there are two columns to put
 * side by side. Stacked, it does not: the leaderboard is as long as it likes,
 * the summary gets whatever is left, and "whatever is left" on a phone was
 * roughly two of the three round rows — the third one, the map of all three
 * guesses, and the play-again button were all inside a panel scrolling
 * invisibly inside a page that could not scroll. Losing a third of the result
 * is not a layout compromise, so below this width the page goes back to being a
 * page.
 */
@media (max-width: 900px) {
  .sg-page.is-results {
    height: auto;
    min-height: 100dvh;
    overflow: visible;
  }

  .sg-results {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
  }

  .sg-results > * {
    overflow: visible;
  }

  /* Stacked, the hairline that divided the columns divides the rows. */
  .sg-results > * + * {
    border-left: none;
    border-top: 1px solid var(--sg-line);
    padding-top: 20px;
  }

  .sg-summary,
  .sg-board {
    padding-right: 2px;
    padding-left: 2px;
  }

  .sg-board {
    margin-top: 18px;
  }

  /*
   * The map goes back to being sized by its width. Filling the leftover height
   * is a two-column idea; in a scrolling column there is no leftover height, and
   * `flex: 1` against `min-height: 120px` collapsed it to a sliver.
   */
  .sg-page.is-results .sg-summary-map {
    flex: none;
    min-height: 0;
    display: block;
  }

  .sg-page.is-results .sg-summary-map svg {
    width: 100%;
    max-height: none;
  }
}

/*
 * Today's result on the tier card, when this browser has already had its go.
 * Stated rather than enforced: knowing is useful, being locked out is not.
 */
.sg-tier-played {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 4px 2px;
  border-bottom: 1px solid transparent;
  font-size: 0.8rem;
  color: var(--sg-muted);
  text-decoration: none;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}

.sg-tier-played:hover,
.sg-tier-played:focus-visible {
  outline: none;
  color: var(--sg-text);
  border-bottom-color: var(--sg-accent);
}

.sg-tier-played strong {
  color: var(--sg-text);
  font-variant-numeric: tabular-nums;
}

.sg-tier-played-moons {
  display: flex;
  gap: 3px;
  font-size: 0.95rem;
  line-height: 1;
}


/* A practice game reaches neither the record nor the board, and says so. */
.sg-practice-note {
  margin: 8px 0 0;
  font-size: 0.82rem;
  color: var(--sg-guess);
}

/* ==========================================================================
 * Phones
 *
 * Three separate axes, deliberately kept apart rather than folded into one
 * "mobile" breakpoint, because they are three different problems:
 *
 *   `pointer: coarse`  — a fingertip instead of a cursor. Governs how big things
 *                        have to be to hit, and which gesture the hints name. A
 *                        touchscreen laptop is coarse at 1400px wide.
 *   `max-width: 700px` — a narrow column. Governs what fits side by side.
 *   `max-height: 520px`— a short frame, which on a phone means it has been
 *                        turned on its side. Governs what fits stacked, and it
 *                        is the one that is easiest to forget and worst to get
 *                        wrong: the guess panel used to hang below the bottom of
 *                        the stage here, taking the submit button with it.
 *
 * A phone in portrait matches the first two, on its side the first and third.
 * ========================================================================== */

/*
 * Pull-to-refresh has no business on a screen that is holding a game, and the
 * overscroll bounce on the pinned screens reads as the layout coming loose.
 * `contain` rather than `none` so the results page, which does scroll on a
 * phone, still scrolls normally.
 */
body {
  overscroll-behavior-y: contain;
}

/* Which zoom gesture the sky hint names. The markup carries both. */
.sg-hint-coarse {
  display: none;
}

/*
 * A card that would hang off the right edge opens to the left of its star
 * instead. The stage clips its overflow, so without this a star in the
 * right-hand quarter of a phone-width sky produced a card with its facts sliced
 * off the screen.
 */
.sg-star-card.is-left {
  transform: translate(calc(-100% - 14px), -50%);
}

@media (pointer: coarse) {
  .sg-hint-fine {
    display: none;
  }

  .sg-hint-coarse {
    display: inline;
  }

  /*
   * Roughly a fingertip. Where a control's size is part of the design — the
   * lesson dots, the masthead links — the *hit* area is grown with a pseudo
   * element instead, so the page looks the same and only the target changes.
   */
  .sg-sky-keys button {
    width: 38px;
    height: 38px;
    font-size: 1rem;
  }

  .sg-layers button {
    padding: 10px;
  }

  /* Clear of the look-around keys, which are 38px tall here rather than 30. */
  .sg-layers {
    top: 58px;
  }

  .sg-panel-close {
    width: 34px;
    height: 34px;
  }

  .sg-how-open {
    padding: 11px 4px;
  }

  .sg-tier-played {
    padding: 10px 2px;
  }

  .sg-setting-choices button {
    padding: 9px 1px;
  }

  .sg-setting-reset {
    padding: 10px 1px;
  }

  .sg-how-close {
    padding: 12px 16px;
    top: 2px;
    right: 4px;
  }

  /* Hit areas grown without moving anything: see the note above. */
  .sg-gear::after,
  .sg-home::after,
  .sg-tutorial-dots button::after {
    content: '';
    position: absolute;
  }

  .sg-gear::after {
    inset: -8px;
  }

  .sg-home::after {
    /* Narrower sideways than the gear's, so the two targets stay apart. */
    inset: -12px -6px;
  }

  .sg-tutorial-dots button {
    position: relative;
  }

  /*
   * Spread first, then grown to fill the space that makes. At the desktop 8px
   * gap the ten dots sit 17px apart, so targets wide enough to be worth having
   * would overlap and a tap near a boundary would land on its neighbour. At 22px
   * they are 31px apart and each one can own its own pitch exactly — ten of
   * those still measure 288px, which fits the narrowest phone.
   */
  .sg-tutorial-dots {
    gap: 22px;
    overflow: visible;
  }

  .sg-tutorial-dots button::after {
    inset: -14px -11px;
  }

  .sg-brand {
    position: relative;
  }

  .sg-brand::after {
    content: '';
    position: absolute;
    inset: -10px -4px;
  }
}

@media (max-width: 700px) {
  /* Every pixel taken back from the margins goes to the sky. */
  .sg-page.is-playing {
    padding: 10px 10px;
    gap: 8px;
  }

  .sg-page.is-playing .sg-header h1,
  .sg-page.is-results .sg-header h1 {
    font-size: 1.15rem;
  }

  .sg-round-bar {
    gap: 4px 14px;
    padding: 8px 12px;
    font-size: 0.87rem;
  }

  /* Pushed to the end of the row so it keeps its own corner when the bar wraps. */
  .sg-score-so-far {
    margin-left: auto;
  }

  /*
   * The rose moves to the top right, which is the one corner a bottom sheet
   * cannot reach.
   *
   * It used to share the bottom-left corner with the drag hint, and the sheet
   * now owns that whole edge — so in Easy, where the compass is a real aid, it
   * disappeared for exactly as long as the map was open. Smaller too: 86 points
   * of rose is a lot of a 368-point sky.
   */
  .sg-compass {
    top: 12px;
    right: 12px;
    bottom: auto;
    left: auto;
    width: 70px;
  }

  .sg-compass svg {
    width: 70px;
    height: 70px;
  }

  /* No rose beside it any more, so the hint takes the whole corner back — just
     high enough to clear the collapsed sheet's title bar. */
  .sg-sky-hint,
  .sg-compass ~ .sg-sky-hint {
    left: 12px;
    bottom: 52px;
    font-size: 0.7rem;
  }
}

/*
 * Sideways. The stage is a letterbox, so anything that was placed by counting
 * pixels down from the top has to come back in.
 */
@media (max-height: 520px) {
  .sg-page.is-playing,
  .sg-page.is-results {
    padding: 8px 12px;
    gap: 6px;
  }

  .sg-page.is-playing .sg-header h1,
  .sg-page.is-results .sg-header h1 {
    font-size: 1.1rem;
  }

  .sg-round-bar {
    padding: 6px 12px;
    font-size: 0.84rem;
  }
}

/*
 * A phone on its side, where the sheet stands along the right edge instead. The
 * bottom-left corner is free again, so the rose goes back to it — stated in
 * full rather than left to inherit, since a narrow-and-short screen matches the
 * portrait rules above as well and would otherwise be pinned top *and* bottom.
 */
@media (min-width: 560px) and (max-height: 520px) {
  .sg-compass {
    top: auto;
    bottom: 8px;
    left: 12px;
    right: auto;
  }

  .sg-sky-hint,
  .sg-compass ~ .sg-sky-hint {
    bottom: 8px;
    left: 110px;
  }
}

/*
 * The settings sheet, stacked.
 *
 * The two-column row puts the label in a 150px cell and the controls in what is
 * left, which on a phone is 105px of slider with four labels under it running
 * off the edge of the sheet. Stacked, the slider gets the full width and its
 * stops line up under it — the 12.5% inset that does that alignment is a
 * function of the stop count, not of the width, so it survives the change.
 */
@media (max-width: 560px) {
  .sg-setting {
    grid-template-columns: 1fr;
    gap: 10px;
    padding: 16px 0;
  }

  .sg-setting dt {
    font-size: 1.02rem;
  }

  .sg-setting-choices {
    gap: 22px;
  }

  .sg-setting-ticks {
    font-size: 0.68rem;
  }

  .sg-how-sheet {
    padding: 22px 18px;
  }
}

/*
 * The explorer's controls, on a narrow stage.
 *
 * These live down here rather than up in the tutorial's own breakpoint because
 * `.sg-explorer`'s base rules are declared further down the file than that
 * block: same specificity, later wins, and a 300px panel stayed 300px wide on a
 * 300px stage. Full width and shorter, which also keeps it clear of the inset
 * map above it once the stage is only 260px tall.
 */
@media (max-width: 820px), (max-height: 520px) {
  /* Above the map's band and the dots above that, still inside the sky. */
  .sg-explorer {
    left: 10px;
    right: 10px;
    bottom: 130px;
    width: auto;
    padding: 8px 10px 7px;
  }

  .sg-explorer-row {
    grid-template-columns: 32px 1fr 58px;
    gap: 6px;
  }

  .sg-explorer-row + .sg-explorer-row {
    margin-top: 5px;
  }

  .sg-explorer-daylight {
    margin-top: 6px;
  }
}

/*
 * Short screens, where the sky is the thing being squeezed.
 *
 * On a 568-point phone the masthead, the round bar and a docked sheet leave the
 * sky about 140 points tall, and a stacked column of three label toggles is 180.
 * The third one was simply behind the sheet. Laid across instead they take one
 * row and clear it — wider than they are tall, which is the shape a thumb
 * prefers anyway.
 *
 * Keyed on height rather than width because this is a height problem: it bites
 * on a tall narrow phone and on the same phone turned sideways, and not at all
 * on a narrow but tall browser window.
 */
@media (max-height: 700px) {
  .sg-layers {
    flex-direction: row;
    flex-wrap: wrap;
    top: 58px;
    right: 12px;
  }

  .sg-layers button {
    padding: 8px 9px;
    font-size: 0.78rem;
    gap: 5px;
  }

  .sg-round-bar {
    padding: 6px 12px;
  }
}
