body {
  margin: 0;
  padding: 0;
  color: rgba(255, 255, 255, 0.87);
  background-color: #000000;
}

#app {
  position: relative;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* The game owns every gesture over the canvas. Without these, dragging a
   selection box starts a native text selection or an image drag, and a touch
   drag pans the page instead of the camera. `touch-action` has to be CSS —
   preventDefault in a passive listener cannot do it. */
#pixi-container,
#pixi-container canvas {
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

/* The outline is suppressed because this is a click target, never a tab stop
   worth showing. The container fills the window and the canvas sits at its
   origin, so a device-sized backing store lands on whole device pixels. */
#pixi-container {
  outline: none;
  position: absolute;
  inset: 0;
  overflow: hidden;
}

#pixi-container canvas {
  display: block;
  position: absolute;
  left: 0;
  top: 0;
}

/* One application, two rendering layers: Pixi owns the game while occasional
   document UI (for example a changelog) can sit above it without an iframe or
   a second site. Empty overlay space passes every gesture through to canvas. */
#html-overlay {
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
}

#html-overlay [data-overlay-interactive] {
  pointer-events: auto;
}

#boot-loading {
  box-sizing: border-box;
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 1.5rem;
  color: #ded3bd;
  background: #16131c;
  font: 16px/1.5 monospace;
  text-align: center;
}

#boot-loading h1 {
  margin: 0;
  color: #d8b45a;
  font-size: 1.5rem;
}

#boot-loading p {
  margin: 0.75rem 0 1.5rem;
}

#boot-loading a {
  padding: 0.35rem 0.75rem;
  border: 1px solid #8f8271;
  color: inherit;
  text-decoration: none;
}

#boot-loading a:focus-visible,
#boot-loading a:hover {
  outline: 2px solid #d8b45a;
  outline-offset: 2px;
}

#fatal-error,
.fatal-error-static {
  box-sizing: border-box;
  width: min(34rem, calc(100vw - 2rem));
  padding: 1.5rem;
  border: 2px solid #8f8271;
  color: #ded3bd;
  background: #16131c;
  box-shadow: 0 1rem 4rem rgba(0, 0, 0, 0.65);
  font: 16px/1.5 monospace;
}

#fatal-error {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

#fatal-error h1,
.fatal-error-static h1 {
  margin: 0 0 1rem;
  color: #c95a50;
  font-size: 1.25rem;
}

#fatal-error p,
.fatal-error-static p {
  margin: 0.5rem 0 1rem;
}

#fatal-error .fatal-error-detail {
  color: #c9a24a;
}

#fatal-error button {
  padding: 0.55rem 0.9rem;
  border: 1px solid #8f8271;
  color: #ded3bd;
  background: #2a2433;
  font: inherit;
  cursor: pointer;
}

#fatal-error button:focus-visible,
#fatal-error button:hover {
  outline: 2px solid #d8b45a;
  outline-offset: 2px;
}

/* The renderer uses the selected capped resolution with autoDensity on, so
   whose devicePixelRatio is not 1 the COMPOSITOR scales the finished canvas up.
   Its default is bilinear, which blurs every pixel edge in the game — measured
   at DPR 2, a hard sky/wall seam came back as a run of blended colours.

   This is not a Retina-only concern: Windows at 150%/175% display scaling and
   Linux fractional scaling give DPR 1.5/1.75 as well. At those ratios a source
   pixel lands on 1 or 2 device pixels alternately, which is the same unevenness
   the 1.5x zoom already has and the look the project settled on.

   Three declarations, ascending: old WebKit and Firefox before 93 only knew the
   first two, and a browser that cannot parse a later value keeps the last one
   it understood. */
#pixi-container canvas {
  display: block;
  image-rendering: -webkit-optimize-contrast;
  image-rendering: crisp-edges;
  image-rendering: pixelated;
}

@font-face {
  font-family: "Sagapora Pixel";
  src: url("/fonts/sagapora-pixel.ttf") format("truetype");
  font-display: swap;
}
/* Title grids (sagapora_14): pixel-exact at 16px or an integer multiple. */
@font-face {
  font-family: "Sagapora Pixel Title";
  src: url("/fonts/sagapora-pixel-title.ttf") format("truetype");
  font-display: swap;
}
/* Display serif (sagapora_display), the canvas `heading` role: pixel-exact
   at 20px or an integer multiple. */
@font-face {
  font-family: "Sagapora Pixel Display";
  src: url("/fonts/sagapora-pixel-display.ttf") format("truetype");
  font-display: swap;
}
/* MenuDialog: native forms and confirmations dressed as ConfirmModal's
   canvas sheet. MenuDialog.ts sets every length as a whole number of UI
   pixels: --u is the CSS size of one UI pixel at the canvas's scale
   (k / devicePixelRatio), and the unitless --edge, --header, --gap, ... are
   the kit's sheet geometry (sheetLayout) for the view's density. It places
   the dialog on a whole CSS and device pixel and paints every framed part
   (sheet, rules, wells, buttons in their state, focus lines) and all text
   from the kit's art and bitmap faces into one device-resolution canvas
   behind the form, so nothing is resampled, seamed or rounded by the
   browser's text layout at fractional ratios. The document text stays for
   assistive technology and shows only as the fallback (data-text="off") or
   where the faces lack a glyph (data-native on the element).
   Colours (--c-<role>) come from the kit's COLOUR roles at runtime; nothing
   here repeats a value. Borders and outlines are never used for pixel lines
   (Chrome floors their widths to whole CSS pixels at fractional DPR). */
.pixel-dialog {
  --u: var(--ui-px, 1px);
  box-sizing: border-box;
  width: calc(var(--dialog-cols, 360) * var(--u));
  max-width: none;
  max-height: calc(var(--dialog-rows, 384) * var(--u));
  /* Taller than a small view: the content scrolls, the painted sheet with
     it (its canvas spans the content). No bar: it would narrow the sheet. */
  overflow: hidden auto;
  scrollbar-width: none;
  margin: auto;
  padding: calc(var(--top, 12) * var(--u)) calc(var(--edge, 32) * var(--u))
    calc(var(--edge, 32) * var(--u));
  border: 0;
  border-radius: 0;
  outline: none;
  background: none;
  isolation: isolate;
  color: var(--c-text);
  font:
    calc(11 * var(--u)) / calc(var(--lh-label, 12) * var(--u)) "Sagapora Pixel",
    monospace;
  font-kerning: normal;
  -webkit-font-smoothing: none;
  -moz-osx-font-smoothing: auto;
  font-smooth: never;
  text-rendering: optimizeSpeed;
  /* A drag over painted text would draw the browser's selection on it;
     fields stay selectable (their selection is a see-through tint). */
  user-select: none;
  -webkit-user-select: none;
}
/* Sized by script: one canvas pixel per device pixel (a whole-pixel box
   scaled down by the ratio, which the browser never resamples). */
.pixel-dialog-chrome {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  pointer-events: none;
  transform-origin: 0 0;
  image-rendering: pixelated;
}
/* Placed by script on a whole CSS and device pixel. */
.pixel-dialog[data-placed] {
  inset: auto;
  margin: 0;
}
.pixel-dialog::backdrop {
  background: var(--c-scrim-a);
}
.pixel-dialog form {
  margin: 0;
}
/* The header band; the title is the canvas `title` role (sagapora_14). */
.pixel-dialog h2 {
  display: flex;
  align-items: center;
  height: calc(var(--header, 48) * var(--u));
  margin: 0;
  overflow: hidden;
  color: var(--c-text);
  font:
    normal calc(16 * var(--u)) / calc(18 * var(--u)) "Sagapora Pixel Title",
    monospace;
  white-space: nowrap;
}
.pixel-dialog-rule {
  height: calc(var(--rule, 2) * var(--u));
  margin: 0 0 calc(var(--gap, 12) * var(--u));
}
/* The footer rule, above the actions. */
.pixel-dialog form > .pixel-dialog-rule:nth-last-child(2) {
  margin: calc(var(--gap, 12) * var(--u)) 0
    calc(var(--footer-gap, 12) * var(--u));
}
.pixel-dialog-actions {
  display: flex;
  justify-content: space-between;
  gap: calc(var(--button-gap, 8) * var(--u));
  height: calc(var(--button-h, 32) * var(--u));
}
/* The message, one line per canvas line (wrapped by script), at the body
   role's pitch. Shown as document text, the browser wraps it, breaking a
   long name anywhere rather than widening the dialog. */
.pixel-dialog p {
  margin: 0;
  overflow: hidden;
  color: var(--c-text);
  line-height: calc(var(--lh-body, 14) * var(--u));
  white-space: pre;
}
.pixel-dialog p[data-native] {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.pixel-dialog-field {
  display: block;
  margin-top: calc(var(--gap, 12) * var(--u));
}
.pixel-dialog-label {
  display: block;
  height: calc(var(--lh-label, 12) * var(--u));
  overflow: hidden;
  color: var(--c-textDim);
  white-space: nowrap;
}
/* Why a field's entry was refused, in the label role and the danger
   colour, one line per canvas line (wrapped by script). */
.pixel-dialog-error {
  display: block;
  margin-top: calc(var(--field-gap, 4) * var(--u));
  overflow: hidden;
  color: var(--c-bad);
  line-height: calc(var(--lh-label, 12) * var(--u));
  white-space: pre;
}
.pixel-dialog-error[hidden] {
  display: none;
}
/* T1 well; its frame, focus line and select chevron are painted. */
.pixel-dialog-well {
  display: block;
  height: calc(var(--well-h, 24) * var(--u));
  margin-top: calc(var(--field-gap, 4) * var(--u));
}
.pixel-dialog-well input,
.pixel-dialog-well select {
  box-sizing: border-box;
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0 calc(var(--field-pad, 8) * var(--u));
  border: 0;
  border-radius: 0;
  outline: none;
  background: transparent;
  color: var(--c-text);
  caret-color: var(--c-text);
  font: inherit;
  line-height: normal;
  appearance: none;
}
.pixel-dialog-well input {
  user-select: text;
  -webkit-user-select: text;
}
.pixel-dialog-select select {
  padding-right: calc(var(--chevron-room, 24) * var(--u));
  cursor: pointer;
}
.pixel-dialog-select option {
  background: var(--c-well);
  color: var(--c-text);
}
/* Buttons: btn_* art by tone and state, painted; widths set by script. */
.pixel-dialog button {
  box-sizing: border-box;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  outline: none;
  color: var(--c-text);
  font: inherit;
  background: none;
  white-space: nowrap;
  cursor: pointer;
}
/* The canvas draws the text: the document's stays for assistive technology,
   and shows where the faces lack a glyph (data-native), so its caret and
   selection match what is seen. A field's selection tints the painted text
   through the highlight. */
.pixel-dialog[data-text="painted"] h2:not([data-native]),
.pixel-dialog[data-text="painted"] p:not([data-native]),
.pixel-dialog[data-text="painted"] .pixel-dialog-label:not([data-native]),
.pixel-dialog[data-text="painted"] .pixel-dialog-error:not([data-native]),
.pixel-dialog[data-text="painted"] input:not([data-native]),
.pixel-dialog[data-text="painted"] select:not([data-native]),
.pixel-dialog[data-text="painted"] button:not([data-native]) {
  color: transparent;
}
.pixel-dialog[data-text="painted"] input::selection {
  background: color-mix(in srgb, var(--c-focus) 30%, transparent);
}
.pixel-dialog[data-text="painted"] input:not([data-native])::selection {
  color: transparent;
}
/* Before the ui atlas loads: flat faces with rim lines. */
.pixel-dialog[data-art="off"] .pixel-dialog-chrome {
  display: none;
}
.pixel-dialog[data-art="off"],
.pixel-dialog[data-art="off"] button,
.pixel-dialog[data-art="off"] .pixel-dialog-well {
  background: var(--c-slot);
  box-shadow: inset 0 0 0 var(--u) var(--c-rim);
}
.pixel-dialog[data-art="off"] .pixel-dialog-well {
  background: var(--c-well);
}
.pixel-dialog[data-art="off"] .pixel-dialog-rule {
  background: var(--c-rim);
}
.pixel-dialog[data-art="off"] button:focus,
.pixel-dialog[data-art="off"] .pixel-dialog-well:focus-within {
  box-shadow: inset 0 0 0 var(--u) var(--c-focus);
}
/* Forced colours (Windows High Contrast) recolour the document's text over
   anything painted: MenuDialog paints nothing (data-art and data-text are
   "off"), the canvas steps aside and system-coloured lines mark the parts. */
@media (forced-colors: active) {
  .pixel-dialog-chrome {
    display: none;
  }
  .pixel-dialog,
  .pixel-dialog button,
  .pixel-dialog-well {
    outline: 1px solid CanvasText;
    outline-offset: -1px;
  }
  .pixel-dialog button {
    outline-color: ButtonText;
  }
  .pixel-dialog[data-art="off"] .pixel-dialog-rule {
    background: CanvasText;
  }
  /* No painted chevron: the system's own. */
  .pixel-dialog-well select {
    appearance: auto;
  }
  .pixel-dialog button:focus-visible,
  .pixel-dialog-well:focus-within {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
}
