/*
 * One stylesheet, hand written, no build step: what is in the repo is what gets uploaded.
 *
 * ---------------------------------------------------------------------------------------------
 * The palette
 *
 * It used to be the game HUD's flat dark green, on the argument that the site should not read as a
 * different product from the thing inside the canvas. It read as a terminal instead: one hue, one
 * value, and every page the same slab of it.
 *
 * What replaces it is a night-blue ground with two accents, which is the pixel-art fantasy vocabulary
 * the maps themselves are drawn in - a cold dark to sit BEHIND artwork rather than compete with it,
 * warm gold for anything that starts a run, cool teal for everything secondary, and parchment rather
 * than white for body text so a page of it is not a light bulb. Gold and teal are never used for the
 * same kind of thing: gold means "this does something", teal means "this is a state".
 *
 * Every one of these is a variable and every page uses them, so the whole site moves together. The
 * numbers were chosen against the ground: parchment on night is ~15:1, dim on night ~7.3:1, gold
 * ~10:1 and teal ~9.6:1 - all past WCAG AA for body text, which matters here because almost every
 * secondary sentence on this site is set in --dim.
 * ---------------------------------------------------------------------------------------------
 */

:root {
  /* Ground and surfaces: night blue, not black, and three steps of it so a panel on a panel reads. */
  --bg: #0d1017;
  --panel: #171d2a;
  --panel-2: #11161f;
  --line: #273044;

  /* Type: parchment, and a cool grey-blue for everything secondary. */
  --ink: #ece4d3;
  --dim: #98a1b5;

  /* The one warm accent. Anything that starts, buys or submits is this colour and nothing else is. */
  --gold: #f0b23c;
  --gold-wash: rgba(240, 178, 60, 0.13);

  /* The cool accent: states, ticks, secondary buttons. */
  --teal: #5fc9c0;
  --teal-wash: rgba(95, 201, 192, 0.12);

  --red: #e2604f;

  /* Kept as an alias so nothing that referred to the old accent by name silently loses its colour. */
  --mint: var(--teal);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-text-size-adjust: 100%;
}

/* The play shell: nothing scrolls, and nothing may bounce or refresh under a thumb (doc 10.8). */
body.bare { overflow: hidden; overscroll-behavior: none; }

a { color: var(--gold); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------------------------------------------------------------- chrome */

.top {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 10px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--panel-2);
}

/*
 * The mark is AruaRPG's own app icon, copied into assets/img rather than re-drawn: the two sites are
 * the same world and one of them already had a real emblem. It is a 192 px pixel PNG shown at 30, so
 * `image-rendering: pixelated` is wrong here for the same reason it is wrong on a map card - it is
 * being scaled DOWN, and the smooth filter is what keeps the wings from aliasing into noise.
 */
.brand { display: flex; align-items: center; gap: 9px; color: var(--ink); font-size: 16px; letter-spacing: 0.02em; }
.brand img { display: block; width: 30px; height: 30px; }
.brand:hover { text-decoration: none; }
.brand .wordmark { line-height: 1.1; }
.brand b { color: var(--gold); }

.tabs { display: flex; gap: 4px; flex: 1; }
.tb { padding: 5px 10px; border-radius: 5px; color: var(--dim); font-size: 14px; }
.tb.on, .tb:hover { background: var(--panel); color: var(--ink); text-decoration: none; }

.who { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.who .av { border-radius: 50%; display: block; }
.who .name { color: var(--dim); }
.who form { margin: 0; }

.btn {
  display: inline-block;
  padding: 6px 12px;
  border: 1px solid var(--teal);
  border-radius: 6px;
  color: var(--teal);
  font-size: 14px;
}
.btn:hover { background: var(--teal-wash); text-decoration: none; }
.btn.play { border-color: var(--gold); color: var(--gold); }
.btn.play:hover { background: var(--gold-wash); }
.btn.play.disabled { opacity: 0.45; cursor: default; pointer-events: none; }
.stat.level .bar { height: 6px; margin-top: 6px; background: var(--line); border-radius: 3px; overflow: hidden; }
.stat.level .bar div { height: 100%; background: var(--gold); }
.stat.level small { display: block; color: var(--dim); font-size: 11px; margin-top: 3px; font-variant-numeric: tabular-nums; }

.link {
  background: none;
  border: 0;
  padding: 0;
  color: var(--dim);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  text-decoration: underline;
}
.link:hover { color: var(--ink); }

/* ---------------------------------------------------------------- pages */

.wrap { max-width: 900px; margin: 0 auto; padding: 24px 18px 64px; }

h1 { font-size: 22px; margin: 0 0 4px; }
h2 { font-size: 16px; margin: 28px 0 8px; }
.lede { color: var(--dim); margin: 0 0 20px; font-size: 14px; }

.panel {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 12px;
}

/* ---------------------------------------------------------------- the map cards */

/*
 * Two or three per row on a desktop, one on a phone: `auto-fill` with a 260 px floor does that on its
 * own against the 900 px wrap, without a media query deciding it in absolute pixels.
 */
.maps { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }

/* The card's own padding is on the parts, not the box, because the preview goes edge to edge. The
   margin `.panel` carries is dropped too: inside a grid the gap is what spaces these, and a margin on
   top of it would space the rows differently from the columns. */
.map { display: flex; flex-direction: column; gap: 0; padding: 0; margin-bottom: 0; overflow: hidden; }
.map h3 { margin: 0; font-size: 16px; text-align: center; }
.map .meta { color: var(--dim); font-size: 13px; text-align: center; }
.map .body { display: flex; flex-direction: column; gap: 7px; padding: 12px 14px 14px; flex: 1; }
.map .row { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: auto; padding-top: 4px; }

/*
 * The preview. `card.png` is the whole map downscaled by the pipeline, so it is NOT pixel art at its
 * native scale any more and `image-rendering: pixelated` would put the aliasing back that the
 * downscale removed - the browser's smooth filter is the right one here, unlike everywhere else on
 * this site.
 *
 * The box keeps its aspect ratio whether or not an image arrived, so a deployment with no previews
 * lays out identically to one with them rather than reflowing when the files turn up.
 */
.map .shot {
  position: relative;
  aspect-ratio: 16 / 9;
  background: linear-gradient(160deg, #1b2233, #10141d);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}
.map .shot img { display: block; width: 100%; height: 100%; object-fit: cover; }

/*
 * A package with no bake, or one whose card.png was never copied in: a flat plate with a word on it
 * rather than a broken image icon, because both are normal states of a deployment mid-upload.
 */
.map .shot.none {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--dim);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/*
 * Locked dims the ARTWORK and puts the tag over it, rather than dimming the whole card: the name, the
 * threshold and the Play button are the things that tell somebody how to unlock it, so those are
 * exactly the things that must not be greyed out.
 */
.map.locked .shot img { opacity: 0.32; filter: grayscale(0.55); }
.map .shot .tag {
  position: absolute;
  top: 8px;
  left: 8px;
  background: rgba(8, 11, 18, 0.8);
  border-color: var(--gold);
  color: var(--gold);
}

/* ---------------------------------------------------------------- the home banner */

/*
 * `hero.png` is the start village of the first playable map. It is a backdrop, so the title sits on a
 * gradient rather than on the picture: a fixed scrim is the only thing that keeps white-ish text
 * legible over artwork nobody has seen yet.
 */
.hero {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  margin-bottom: 18px;
  background: var(--panel-2);
}
.hero img { display: block; width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
.hero .scrim {
  position: relative;
  padding: 26px 22px;
  min-height: 150px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 8px;
  background: linear-gradient(90deg, rgba(8, 11, 18, 0.92) 0%, rgba(8, 11, 18, 0.72) 55%, rgba(8, 11, 18, 0.35) 100%);
}
.hero h1 { margin: 0; font-size: 24px; }
.hero p { margin: 0; color: var(--dim); font-size: 14px; max-width: 40em; }
.hero .row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }

/* The banner's Play is the page's primary action, so it gets the same 44 px target the sign-in has. */
.btn.big {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 18px;
  font-size: 15px;
}

.stats { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; }
.stat b { display: block; font-size: 20px; font-variant-numeric: tabular-nums; }
.stat span { color: var(--dim); font-size: 12px; }
/* A tile whose value is a word rather than a number - a skill's name, a one-word verdict. 20px is
   sized for four digits and wraps a name into three lines; the numbers stay the loud ones. */
.stat b.word { font-size: 15px; line-height: 1.35; }

table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); }
th { color: var(--dim); font-weight: 400; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
td.n, th.n { text-align: right; font-variant-numeric: tabular-nums; }
tr:last-child td { border-bottom: 0; }

.rank { color: var(--dim); width: 2em; }
/* A player, as a face and a name. It is an <a> on the boards - every row links to that account's own
   page - so it takes the page's ink rather than the link gold, with the underline saved for hover:
   twenty gold rows would read as twenty warnings. */
.pl { display: flex; align-items: center; gap: 8px; color: var(--ink); }
.pl img { border-radius: 50%; display: block; }
.pl:hover { color: var(--gold); }

.note {
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: 6px;
  background: var(--panel);
  padding: 10px 12px;
  color: var(--dim);
  font-size: 13px;
  margin: 0 0 16px;
}

.tag {
  display: inline-block;
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--dim);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  vertical-align: middle;
}
.tag.unverified { border-color: #6f5520; color: #d6a840; }
/* The verifier's three visible states (doc 10.1). Pending is deliberately the quiet one: it is not a
   suspicion about the run, it is this site not having asked the other machine yet. */
.tag.verified { border-color: #2c5b57; color: var(--teal); }
.tag.pending  { border-color: var(--line); color: var(--dim); }
.tag.rejected { border-color: #6d2a24; color: var(--red); }

.ok { color: var(--teal); }

.empty { color: var(--dim); font-size: 14px; }
.dim { color: var(--dim); }

.note.good { border-left-color: var(--teal); color: var(--ink); }
.note.bad { border-left-color: var(--red); color: var(--ink); }

/* ---------------------------------------------------------------- one run's public page (doc 10.7) */

.runhead { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.runhead h1 { margin: 0 0 2px; }
.runhead h1 a { color: inherit; }
.runhead h1 a:hover { color: var(--gold); }
.runhead .face { border-radius: 50%; display: block; }
.runhead a { line-height: 0; }

/* "This is you" on your own public page. Teal rather than gold: it is an orientation, not an offer,
   and the only thing on the page that depends on who is looking. */
.you { color: var(--teal); }

.runshot { padding: 0; overflow: hidden; }
.runshot img { display: block; width: 100%; max-height: 220px; object-fit: cover; }

/*
 * The line the player pasted into a chat, shown back to them at the top of what it linked to. Set in
 * the monospace stack so the emoji and the separators line up the way they did in the message, and
 * allowed to wrap rather than scroll: it is a sentence, not a code sample.
 */
.runline {
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: 6px;
  background: var(--panel);
  padding: 10px 12px;
  margin: 0 0 16px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.7;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------- the passive tree (doc 10.9) */

/*
 * The board is the real tree artwork with a node over each of the source game's own node positions,
 * so the layout is the game's rather than something re-invented here - but it is drawn to FIT THE
 * COLUMN rather than at the artwork's native 512 px, which used to leave it occupying about half the
 * page with dead space either side.
 *
 * What makes that work is that nothing here is in pixels any more. The box takes the artwork's aspect
 * ratio (written inline, since only the tree file knows it), the artwork is stretched over it, and the
 * view positions every node as a percentage - so the geometry is identical at any width and there is
 * no second copy of the layout to keep in step. `image-rendering: pixelated` is what keeps a 2x
 * upscale of pixel art crisp instead of blurred, and the inline `max-width` stops the upscale going
 * past 2x on a very wide screen.
 *
 * The horizontal scroller this wrapper used to need is gone with the fixed width: a board that is
 * 100% of its column cannot push the document sideways on a phone.
 */
.treeboard { padding-bottom: 6px; }

/*
 * The board, and why it is a panel rather than a picture.
 *
 * It used to be the source game's own 512 px artwork with a node placed over each position. That was
 * wrong twice: the artwork paints its OWN 40 px icon into every slot, so our 24 px cut sat on top of a
 * larger one and read as a rendering fault, and the picture shows about thirty slots this game has no
 * nodes for - most of what a player was looking at could never be learned, and nothing said so. What
 * is drawn now is exactly what exists.
 *
 * Everything inside is a percentage of this box: the positions, the node size, and the icon sprite.
 * `aspect-ratio` comes from the tree file, so the geometry is the source game's own and the board
 * still fills whatever column it is given. `max-width` stops a very wide screen turning a 512 px
 * layout into a wall.
 */
/* The board: one row per chain, laid out by the prerequisites (Tree::chains), never by the source
   artwork's coordinates. A row is the root, an arrow, then the nodes it unlocks side by side. */
.treeart {
  display: flex;
  flex-direction: column;
  gap: 18px;
  width: 100%;
  max-width: 1024px;
  margin: 0 auto;
  padding: 18px 20px;
  box-sizing: border-box;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-2);
}
.trow { display: flex; align-items: flex-start; flex-wrap: wrap; gap: 12px; }
.tkids { display: flex; align-items: flex-start; flex-wrap: wrap; gap: 12px; }
.tbranch { display: flex; align-items: flex-start; gap: 12px; }
.tarrow { align-self: flex-start; margin-top: 22px; color: var(--dim); font-size: 22px; line-height: 1; user-select: none; }
.tcell { display: flex; flex-direction: column; align-items: center; width: 92px; }
.tname { margin-top: 10px; font-size: 11px; line-height: 1.25; text-align: center; color: var(--dim); }

.tnode {
  position: relative;
  display: block;
  width: 64px;
  height: 64px;
  box-sizing: border-box;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel);
  color: var(--dim);
  text-decoration: none;
}
.tnode:hover, .tnode:focus { outline: 2px solid var(--gold); text-decoration: none; z-index: 3; }

/* The icon fills the padded box. Its background-size and background-position are percentages written
   inline by the view out of the atlas rectangle, which is what lets a 24 px sprite scale with a node
   whose own size is a fraction of the board. Pixelated, because this is pixel art. */
.tnode .ic { position: absolute; inset: 9%; display: block; image-rendering: pixelated; }

/* The level, in the corner rather than under the icon: a node is small and a badge inside it would
   take the space the picture needs. Fixed size, since a percentage of a small box is unreadable. */
.tnode .lv {
  position: absolute;
  right: -4px;
  bottom: -5px;
  padding: 0 3px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg);
  font-size: 9px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
}

/* The four states the row below spells out in words. Colour alone is never the only signal: every
 * node carries its level, a tooltip, and a row in the table with the reason written out. */
.tnode.open { border-color: var(--gold); color: var(--gold); }
.tnode.open .lv { border-color: var(--gold); }
.tnode.maxed { border-color: var(--teal); color: var(--teal); }
.tnode.maxed .lv { border-color: var(--teal); }
.tnode.poor { border-color: #6f5520; }
.tnode.locked { opacity: 0.45; }

/*
 * The tooltip, which is markup rather than a `title` attribute for three reasons: a native tooltip
 * cannot be styled, cannot be opened from a keyboard, and cannot be UPDATED - after a purchase the
 * script rewrites these fields in place, and a `title` would have gone on naming the old level and
 * the old price until the page was reloaded.
 *
 * Centred over the node with a translate rather than positioned by script: there is no measuring to
 * do, nothing to recompute on scroll or resize, and it cannot end up somewhere the node is not.
 * `pointer-events: none` keeps it from eating the hover that opened it.
 */
.ttip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%);
  z-index: 4;
  display: none;
  width: max-content;
  max-width: 220px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  color: var(--ink);
  font-size: 12px;
  line-height: 1.45;
  text-align: left;
  pointer-events: none;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}
.ttip b { display: block; color: var(--ink); font-size: 13px; }
.ttip span { display: block; color: var(--dim); }
.ttip .teff { color: var(--teal); }
.ttip .tcost { color: var(--gold); font-variant-numeric: tabular-nums; }
.ttip .teff:empty, .ttip .tcost:empty { display: none; }
.tnode:hover .ttip, .tnode:focus .ttip, .tnode:focus-visible .ttip { display: block; }
/* A node in the top row would open its tooltip off the top of the board, so those flip underneath.
   The view does not have to know which: 18% of the board is two rows of nodes at any width. */
.tnode.high .ttip { bottom: auto; top: calc(100% + 8px); }

/* One 24 px cut out of passive-tree-icons.png at its own size, for the table below the board. Size
 * and background-position are written inline, because they come out of the pack's own atlas
 * rectangle. */
.ic {
  display: inline-block;
  flex: none;
  background-repeat: no-repeat;
  image-rendering: pixelated;
  vertical-align: middle;
}

/* What a refused purchase says, beside the button that was pressed rather than at the top of a page
   the player would have to scroll back up to. */
.learnsaid { display: block; color: var(--red); font-size: 12px; text-align: right; }
.learnsaid:empty { display: none; }

.treelist td { vertical-align: top; }
.treelist tr.locked { opacity: 0.65; }
.treelist form { margin: 0; }
.treelist .pl { gap: 8px; }

/* `.btn` was written for an <a>; the tree's Learn control is a real submit button, so it needs the
 * three properties a button does not inherit. */
.btn.small {
  padding: 4px 9px;
  font-size: 13px;
  white-space: nowrap;
  background: none;
  font-family: inherit;
  cursor: pointer;
}

/* ---------------------------------------------------------------- the character (doc 10.10) */

/*
 * A composed still: one box per layer, each exactly one 64 px sheet cell, stacked in source order
 * (which the view has already sorted by the facing's own draw order) and magnified by an integer
 * factor from the top-left corner.
 *
 * `transform: scale()` rather than `background-size`, because the sheet is 256x512 and only its first
 * cell is wanted: scaling the box leaves `background-position: 0 0` meaning frame 0, facing 0, while
 * scaling the background would have to restate the whole sheet's size at every magnification. The
 * outer box carries the FINAL size, since a scaled child still occupies its unscaled space in layout.
 */
.avatar { position: relative; overflow: hidden; flex: none; }

.avatar .al {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  background-repeat: no-repeat;
  background-position: 0 0;
  image-rendering: pixelated;
}

.charsheet { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.charsheet .avatar { border: 1px solid var(--line); border-radius: 8px; background: var(--panel); }
.charfacts { flex: 1; min-width: 260px; }
.charfacts th { width: 11em; vertical-align: top; }
.charfacts td { font-variant-numeric: tabular-nums; }

/*
 * The signed-out panel on / and the guest half of /character.
 *
 * No new layout of its own: it is a `.charsheet` - the same picture-beside-facts row the signed-in
 * character page uses - with the facts replaced by a sentence and a button, so a guest and a player
 * are looking at the same object at two sizes rather than at two designs.
 */
/* Restyled with the rest of the site rather than rebuilt: the panel takes the new surfaces from the
   variables, and the one thing added is the gold edge that marks it as the page's primary offer -
   the same signal the banner carries for somebody who already has an account. */
.guest { border-left: 3px solid var(--gold); background: var(--panel); }
.guest .guestsay { flex: 1; min-width: 240px; }
.guest .guestsay p { margin: 0 0 10px; }
.guest .guestsay p:last-child { margin-bottom: 0; }

/*
 * The sign-in button is the one control on either page, so it is a target rather than a link that
 * happens to have a border: 44 px is the smallest thing a thumb reliably hits, and `inline-flex` with
 * a min-height is what actually gives an <a> that height - padding alone on an inline-block leaves the
 * line box deciding it. Gold rather than mint, because it is the primary action here the way Play is
 * on a map card.
 */
.btn.signin {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  border-color: var(--gold);
  color: var(--gold);
  font-size: 15px;
}
.btn.signin:hover { background: var(--gold-wash); }

/*
 * The character at the top of the home page (doc 10.10).
 *
 * Centred and above everything else for a signed-in player, and the WHOLE page for a signed-out one -
 * `.charhero.guest` is a picture and a button in the middle of an otherwise empty column, because
 * everything that used to sit under it needs an account to mean anything.
 *
 * The guest version is vertically centred with `min-height: 70vh` rather than being pushed down with
 * a margin, so it sits in the middle of a phone screen and the middle of a desktop one without a
 * breakpoint deciding which. The picture itself is sized in the view - 512 px signed out, 256 signed
 * in - because only the view knows which of those it is drawing.
 */
.charhero { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-bottom: 24px; }
.charhero a { display: block; line-height: 0; }
.charhero .avatar { border: 1px solid var(--line); border-radius: 10px; background: var(--panel); }
.charhero.guest { min-height: 70vh; justify-content: center; gap: 22px; margin-bottom: 0; }

/* The controls under the numbers on /character: two links out and, for an admin, the one form. A
   flex row rather than a paragraph, so the form's button sits on the same line as the links instead
   of on a line of its own. */
.charactions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.charactions form { margin: 0; }

/* The small copy beside the name on /me. The heading keeps its own margins, so the row is aligned on
 * the baseline-ish middle rather than on the top of a picture that is taller than the text. */
.whoami { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.whoami h1 { margin: 0; }
.whoami a { display: block; line-height: 0; }

.centre { max-width: 34em; margin: 16vh auto; padding: 0 18px; text-align: center; }
.centre h1 { font-size: 20px; }
.centre p { color: var(--dim); font-size: 14px; }

/* ---------------------------------------------------------------- the Share control */

/*
 * The field is not decoration: it is the fallback path. `navigator.clipboard` is absent over plain
 * HTTP and refuses in some embedded browsers, and when it does the script focuses and selects this
 * input instead - so it has to be on the page rather than created on failure, and it has to be wide
 * enough that the selected text is visibly the whole URL.
 */
.share { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.share label { flex: none; }
.shareurl {
  flex: 1;
  min-width: 220px;
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--dim);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
}

/* ---------------------------------------------------------------- /admin */

/*
 * Deliberately the plainest pages on the site. They are a table, a search box and a form, drawn out of
 * the same variables and the same `.panel` / `.btn` / `table` rules everything else uses - an admin
 * area with a look of its own would be a second design to keep in step for an audience of one or two.
 *
 * The only thing here that is not already defined above is what a form needs: a label above its field
 * rather than beside it, so a long hint sits under the box instead of pushing it sideways.
 */
.adminsearch { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.adminsearch input, .adminsearch select {
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
}

.adminform p { margin: 0 0 14px; display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.adminform label { color: var(--dim); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
.adminform input[type="text"] {
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  min-width: 16em;
}
.adminform h3 { font-size: 14px; margin: 22px 0 8px; }
.adminform .checks { list-style: none; margin: 0; padding: 0; }
.adminform .checks li { margin-bottom: 4px; font-size: 14px; }
.adminform .checks label { text-transform: none; letter-spacing: 0; font-size: 14px; color: var(--ink); }
.adminform .treelevels input { width: 4em; min-width: 0; text-align: right; }
.adminform .treelevels label { text-transform: none; letter-spacing: 0; font-size: 14px; color: var(--ink); }

/* The two destructive actions, each in its own form so a stray Enter in the edit form cannot reach
   them. Stacked rather than in a row, because each carries a sentence explaining what it discards. */
.adminactions form { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }

.mono, .logdata { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; }
/* A before/after pair can be long. It wraps inside its cell rather than pushing the table sideways. */
.logs .logdata { display: block; max-width: 34em; color: var(--dim); overflow-wrap: anywhere; }
.pager { display: flex; align-items: center; gap: 10px; margin-top: 14px; }

/* ---------------------------------------------------------------- play shell */

/*
 * The React game mounts as `position: fixed; inset: 0`, so anything that has to stay visible over it
 * needs its own stacking context above. This bar is the only such thing.
 */
.playbar {
  position: fixed;
  top: 10px;
  right: 12px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  border-radius: 8px;
  background: rgba(8, 11, 18, 0.76);
  font-size: 13px;
  color: var(--dim);
}
.playbar a { color: var(--gold); display: inline-flex; align-items: center; min-height: 28px; }
.playbar .name { color: var(--ink); }

/*
 * On a touch screen the game's own controls (doc 10.8) claim the left half for the virtual stick and
 * the bottom right for the skill buttons, and the game HUD is top left. The bar keeps the one corner
 * left over and shrinks to a chip: the player name and the long half of each label go, because the
 * shell must never be the thing a thumb hits when it means to move.
 */
@media (pointer: coarse), (max-width: 700px) {
  .playbar { top: 6px; right: 6px; gap: 8px; padding: 4px 8px; font-size: 12px; max-width: 44vw; }
  .playbar .name { display: none; }
  .playbar .wide { display: none; }
  .playbar a { min-height: 36px; padding: 0 4px; }
}

/* touch-action / overscroll: no double-tap zoom and no pull-to-refresh over a live run. */
#game { position: fixed; inset: 0; touch-action: none; overscroll-behavior: none; }

/* Shown until the bundle boots, and replaced by it. A blank screen with nothing on it looks broken. */
#boot { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--dim); }
