/* Emberfall - dark pixel-fantasy UI */
* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0; height: 100%; overflow: hidden;
  background: #0d0c10; color: #e8e6ef;
  font-family: "Segoe UI", system-ui, sans-serif; font-size: 13px;
  user-select: none;
}
/* ============ ONE SIZE FOR EVERY SCREEN ===================================
   Owner, 2026-10-10: "Implement UI scaling of text, so it looks properly
   sized on different" screens. The whole interface is zoomed by --uiz
   (client/js/uiscale.js: Auto from the window size, or Menu > Interface >
   UI size) and the world is zoomed straight back, so its pixel art and the
   clicks on it are exactly what they were. --uiz is 1 while
   CONFIG.UI_SCALE_OPEN is off. vh and vw are zoomed too, so a length that
   must be a share of the SCREEN is written calc(Nvh / var(--uiz, 1)). */
body { zoom: var(--uiz, 1); }
#game { zoom: calc(1 / var(--uiz, 1)); }
/* ============ A CURSOR YOU CAN SEE ON STONE =============================
   `crosshair` is the operating system's, and the operating system's is a
   thin black cross. On grass it is fine. On a grey road, a stone floor, a
   cave wall or a rainy sky it is very nearly gone - and this is a game where
   the cursor is the aim, so losing it is losing the thing you are pointing
   with.

   Drawn here instead, twice over: a fat dark stroke laid down first and a
   thin white one on top of it. That pairing is what makes it readable on
   ANY background rather than on the ones we happened to test - white shows
   against the dark ground, the dark outline shows against the light, and
   there is no colour it can lose to. The arms leave a gap in the middle so
   the tile under it is never hidden by the thing pointing at it, and a small
   dot marks the exact spot. The hotspot is the centre, 12 12.

   Kept as a data URI so it ships inside the asar with everything else - no
   file to lose, and nothing fetched at runtime. */
/* F6: CRISP AT 125-150% WINDOWS SCALING. The canvas is one canvas pixel per
   CSS pixel (render.js, dpr = 1), so on a scaled laptop screen the browser
   stretches it by 1.25 or 1.5 - and it stretched it SMOOTHLY, which turns
   pixel art to mush. Nearest-neighbour keeps every pixel a hard square. At
   100% scaling this changes nothing at all: the stretch is 1:1. */
#game {
  image-rendering: pixelated;
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><g fill='none' stroke='%23000' stroke-opacity='0.85' stroke-width='4.5' stroke-linecap='round'><path d='M12 2.5v6M12 15.5v6M2.5 12h6M15.5 12h6'/></g><g fill='none' stroke='%23fff' stroke-width='1.7' stroke-linecap='round'><path d='M12 2.5v6M12 15.5v6M2.5 12h6M15.5 12h6'/></g><circle cx='12' cy='12' r='1.5' fill='%23fff' stroke='%23000' stroke-opacity='0.85' stroke-width='1.1'/></svg>") 12 12, crosshair;
}
.hidden { display: none !important; }
.muted { color: #8b8898; font-size: 12px; line-height: 1.5; }
button {
  background: #24222e; color: #e8e6ef; border: 1px solid #3a3748;
  padding: 6px 10px; border-radius: 4px; cursor: pointer; font-size: 12px;
}
button:hover { background: #2f2c3c; border-color: #55506a; }
button:disabled { opacity: .38; cursor: default; }
input, select {
  background: #191823; color: #e8e6ef; border: 1px solid #34313f;
  padding: 6px 8px; border-radius: 4px; font-size: 13px; width: 100%;
}
/* ============ AND A TEXTAREA IS AN INPUT TOO ==========================
   It was never in that list, so every textarea in the game rendered as the
   browser's default: a white box with black monospace type, in the middle
   of a window that is otherwise dark. Nobody noticed because until now the
   only ones were in the crash window, which people see once.

   The width is deliberately NOT set here. `input` takes 100% because every
   input in this game is a full-width field; textareas are also used inside
   the Studio's own panels, which set their own, and a blanket 100% would
   reach in there and push them about. The Studio's tools.css is loaded
   after this file and scopes `.field textarea`, so it wins regardless -
   but the look should not depend on load order to be correct. */
textarea {
  background: #191823; color: #e8e6ef; border: 1px solid #34313f;
  padding: 6px 8px; border-radius: 4px; font-size: 13px;
  font-family: inherit; resize: vertical;
}
textarea:focus, input:focus, select:focus { outline: none; border-color: #55506a; }

/* ------------------------------------------------------ pixel scrollbars */
/* The browser's own bars are rounded, pale and soft-edged - a web page
   bolted onto a pixel game. These are built the way the rest of the UI is:
   square corners, hard 1px edges, a lit top-left and a shaded bottom-right
   so the thumb reads as a raised tile, and a notched grip down the middle.
   Nothing here is anti-aliased, so it stays crisp at any window size.
   It is one global rule, so every list, panel, vault and modal gets it. */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track {
  background: #0c0b10;
  box-shadow: inset 0 0 0 1px #1d1b26;      /* a sunken channel */
}
::-webkit-scrollbar-corner { background: #0c0b10; }
/* the default end arrows are rounded and cannot be redrawn cleanly - drop them */
::-webkit-scrollbar-button { display: none; height: 0; width: 0; }
::-webkit-scrollbar-thumb {
  background-color: #3a3748;
  border: 1px solid #16151d;                /* keeps it off the channel wall */
  border-radius: 0;
  box-shadow: inset 1px 1px 0 #5f5978,      /* lit corner */
              inset -1px -1px 0 #24222e;    /* shaded corner */
  background-repeat: no-repeat;
  background-position: center;
}
/* three notches across the middle - drawn as flat pixel rows, no blur */
::-webkit-scrollbar-thumb:vertical {
  background-image: repeating-linear-gradient(
    #23212e 0 1px, #5f5978 1px 2px, transparent 2px 3px);
  background-size: 6px 9px;
  min-height: 28px;
}
::-webkit-scrollbar-thumb:horizontal {
  background-image: repeating-linear-gradient(90deg,
    #23212e 0 1px, #5f5978 1px 2px, transparent 2px 3px);
  background-size: 9px 6px;
  min-width: 28px;
}
::-webkit-scrollbar-thumb:hover {
  background-color: #4e4866;
  box-shadow: inset 1px 1px 0 #7d7599, inset -1px -1px 0 #2b2839;
}
/* dragging it lights up in the same ember the rest of the game uses */
::-webkit-scrollbar-thumb:active {
  background-color: #7a3a1a;
  box-shadow: inset 1px 1px 0 #d0822f, inset -1px -1px 0 #45200e;
}

/* ------------------------------------------------------------- overlays */
/* THE VALE ITSELF, BEHIND THE DOOR. A dark scrim sits over the picture and a
   heavier one pools in the middle, because the login box is 460px of small
   grey text and a 44px gold title, and a night valley is exactly busy enough
   to eat both. The scrim is a gradient rather than a flat wash so the corners
   of the painting still read - you can see the mountains and the village, you
   just cannot see them THROUGH the words.

   `pixelated` matters: the picture is a pixel-art painting and the browser
   would otherwise smooth it as it scales to the window, which turns crisp
   blocks into mush at exactly the size a login screen is looked at. */
.overlay {
  position: absolute; inset: 0; z-index: 50;
  /* A SCRIM MUST NOT HAVE AN EDGE. This was a 55% x 60% ellipse over the
     middle, which on a bright painting reads as a dark BOX sitting behind the
     title - you see the shape of the help rather than the help. It is a full
     bleed wash now, heaviest at the top and bottom where the text sits and
     barely there across the middle, so there is no line anywhere for the eye
     to catch on. Legibility comes from the letters' own shadow instead. */
  background:
    linear-gradient(to bottom, #08070be0 0%, #08070b55 26%, #08070b30 52%, #08070b88 100%),
    url('login-bg.jpg') center / cover no-repeat,
    radial-gradient(ellipse at center, #16141f 0%, #08070b 100%);
  image-rendering: pixelated;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
}
/* and the title gets a shadow it can actually stand on */
.loginbox h1 { text-shadow: 0 0 24px #e8702c55, 0 3px 10px #000c, 0 1px 0 #000; }
.loginbox { width: 460px; display: flex; flex-direction: column; gap: 10px; }
/* the title is a canvas of pixels now, not text - `pixelated` so the browser
   never softens it, and a warm bloom under it so it sits in the firelight of
   the picture behind rather than on top of it */
.titlecanvas {
  display: block; margin: 0 auto 6px; max-width: 100%; height: auto;
  image-rendering: pixelated;
  /* the bloom follows the letters, not the canvas - drop-shadow reads alpha,
     so a transparent canvas casts a title-shaped glow and no rectangle */
  filter: drop-shadow(0 0 22px #e8702c55) drop-shadow(0 0 6px #00000099);
}
/* ============ THIS TEXT SITS ON A PAINTING ==========================
   Everything on the login overlay is laid over a lit painting of the vale -
   dark stone in some places, firelight in others - and it was coloured as
   though it were sitting on the game's flat panel grey. #7d7a8c on a dark
   blue-grey tile is roughly a 2:1 contrast ratio: legible on a swatch,
   invisible in practice, and the tagline disappeared completely.

   Two changes, and the second matters more than the first. The colours come
   up toward the parchment the title is drawn in, and every line gets a dark
   halo. The halo is what actually does the work: the background is not one
   colour, so no single text colour can be safe against all of it. A shadow
   travels with the letters and gives them their own ground wherever they
   land. */
.loginbox .sub {
  margin: -8px 0 12px; color: #cfc3a8; letter-spacing: 2px;
  text-shadow: 0 1px 2px #000, 0 0 6px #000c;
}
.loginbox label {
  display: flex; flex-direction: column; gap: 4px; font-size: 12px;
  color: #d3cbbc; text-shadow: 0 1px 2px #000, 0 0 5px #000a;
}
/* the headings over the server and character lists, and the quiet notes
   under them - same painting, same problem */
.loginbox .rowhead, .loginbox .serverhead, .loginbox .muted,
#laninfo, #onlineinfo, .servercard .sblurb {
  color: #c3bbac; text-shadow: 0 1px 2px #000, 0 0 5px #000a;
}
#loginstatus { text-shadow: 0 1px 2px #000, 0 0 6px #000c; }
.servercard b, .charcard .cname { text-shadow: 0 1px 2px #000, 0 0 5px #000a; }
.loginbox #btn-play { margin-top: 8px; padding: 10px; font-size: 15px; background: #7a3a1a; border-color: #a8542a; }
.loginbox #btn-play:hover { background: #94481f; }
#loginstatus { min-height: 18px; color: #e8a04a; font-size: 12px; }
/* ------------------------------------------- the door, in its own livery --
   THE LOGIN SCREEN SHOULD BE MADE OF WHAT THE TITLE IS MADE OF. It was the
   game's ordinary purple-grey panel furniture sitting under a gold-and-stone
   brand mark - two different games stacked on top of each other. Everything
   here is now the mark's own materials: dark stone, a lit top edge and a
   shadowed underside on anything raised (the same bevel the letters use), and
   amber for the one thing you are meant to press.

   Deliberately NOT applied past this screen. In the world the interface has
   to disappear behind the game; here it IS the game, and it can be dressed. */
.loginbox label { color: #b8ae9a; letter-spacing: .04em; }
.loginbox input {
  background: #14110d; border: 1px solid #3c332a;
  border-top-color: #241d17; border-bottom-color: #57493a;
  color: #f0e4cc; padding: 8px 10px;
}
.loginbox input:focus { outline: none; border-color: #8a6a34; box-shadow: 0 0 0 1px #e8a04a33; }
.loginbox button {
  padding: 9px; font-size: 14px; letter-spacing: .06em;
  background: #2a231b; color: #d8cbb4;
  border: 1px solid #4a3d2e; border-top-color: #6b5941; border-bottom-color: #221b14;
}
.loginbox button:hover { background: #362c21; color: #f0e4cc; border-color: #6b5941; }
.loginbox button.ghost {
  background: transparent; border-style: dashed; border-color: #4a3d2e;
  font-size: 12px; padding: 7px; color: #a2947c;
}
.loginbox button.ghost:hover { color: #e8c14a; border-color: #6b5941; }
.loginbox button.link { background: none; border: none; color: #8a7f6c; font-size: 11px; padding: 2px 4px; }
/* EXIT GAME - owner, 2026-09-30: "much more visible, and in the bottom left
   corner". Off the panel and pinned to the corner of the screen on every
   login step, big and ember-red so it can never be mistaken for the way in
   (the host's two icons have the bottom right). */
#login > button.exitgame {
  position: absolute; left: 22px; bottom: 22px; z-index: 4;
  padding: 12px 28px; border-radius: 4px;
  font-size: 17px; font-weight: bold; letter-spacing: .12em; text-transform: uppercase;
  color: #ffe6d8; background: #5a2419;
  border: 2px solid #c0513c; border-top-color: #e07a62; border-bottom-color: #7a2c1f;
  box-shadow: 0 3px 14px #000c, 0 0 18px #c0513c44;
  text-shadow: 0 1px 2px #000a;
}
#login > button.exitgame:hover { background: #7a2e1f; border-color: #f08a6e; color: #fff; box-shadow: 0 3px 14px #000c, 0 0 24px #e0704f77; }
#login > button.exitgame:focus-visible { outline: 2px solid #ffd2b8; outline-offset: 3px; }
.loginbox button.link:hover { color: #e8c14a; }
/* the one you are here to press - lit like the letters, and it is the only
   thing on the screen wearing gold */
#btn-connect, #btn-play, #btn-create {
  background: linear-gradient(#a8641f, #7a3a13);
  border: 1px solid #c07a2a; border-top-color: #e8b060; border-bottom-color: #5a2a0e;
  color: #fff3d8; font-weight: 600; text-shadow: 0 1px 0 #00000066;
}
#btn-connect:hover, #btn-play:hover, #btn-create:hover {
  background: linear-gradient(#c07726, #94481a); border-top-color: #ffd08a;
}
/* ============ THE LOGIN SCREEN READS ==================================
   "Your characters", "change server" and "+ New character" were all set in
   the same dim brown as the panel they sit on - #8a7f6c on #1a1611 - which
   is the colour of something disabled. The heading is white now, the link
   beside it is legible, and the new-character box looks like a box you can
   press rather than a hint that one might exist. */
.rowhead { color: #ffffff !important; font-weight: 600; }
.loginbox button.link { color: #cfc3ad; }
.loginbox button.link:hover { color: #ffd98a; }
#btn-new {
  color: #f2e8d4; border-style: dashed; border-color: #6b5941;
  background: #241e17; font-size: 13px; padding: 9px;
}
#btn-new:hover { color: #ffd98a; border-color: #c07a2a; background: #2e261c; }

#roster .charcard { background: #1a1611; border-color: #3c332a; }
#roster .charcard:hover { border-color: #6b5941; background: #221c15; }
#roster .charcard.on { border-color: #c07a2a; box-shadow: 0 0 0 1px #e8a04a44 inset; }
#loginstatus { color: #e8c14a; text-align: center; }
/* the host's corner - square, quiet, and out of the way of the door */
.hostcorner {
  /* FIXED, NOT ABSOLUTE. `absolute` pins to the nearest positioned ancestor,
     and the corner lives inside .loginbox - which had just been given
     `position: relative` so it could sit above the light layer. So it pinned
     to the bottom right of the PANEL, which is the middle of the screen.

     `fixed` pins to the window itself and cannot be captured by an ancestor,
     which is what "bottom right of the window" actually means. It disappears
     with the login screen because it is inside it and that gets hidden. */
  position: fixed; right: 16px; bottom: 16px;
  display: flex; align-items: center; gap: 8px; z-index: 60;
}
.iconbtn {
  width: 40px; height: 40px; padding: 0; display: flex; align-items: center; justify-content: center;
  background: #2a231b; border: 1px solid #4a3d2e;
  border-top-color: #6b5941; border-bottom-color: #221b14; border-radius: 6px;
  cursor: pointer;
}
.iconbtn:hover { background: #362c21; border-color: #6b5941; }
.iconbtn svg { width: 21px; height: 21px; fill: none; stroke: #a2947c; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; }
.iconbtn:hover svg { stroke: #e8c14a; }
/* ON is the whole point of a power button, so it is unmistakable: the frame
   lights, the mark goes gold, and it glows a little. */
/* LIT. Bone-white to hot amber, and the whole button glows - a power symbol
   that is on should look like a lamp that is on, not like a button with a
   slightly different border. */
.iconbtn.on {
  background: #3a2a12; border-color: #b5852f;
  box-shadow: 0 0 14px #e8a04a66, 0 0 30px #e8702c2e, inset 0 0 0 1px #e8a04a33;
}
.iconbtn.on svg { stroke: #ffd06a; filter: drop-shadow(0 0 5px #e8a04aaa); }
.iconbtn.starting { background: #34260f; border-color: #8a6a2a; animation: hostwarm 1.1s ease-in-out infinite; }
.iconbtn.starting svg { stroke: #e8b45a; }
@keyframes hostwarm {
  0%, 100% { box-shadow: 0 0 6px #e8a04a33; }
  50%      { box-shadow: 0 0 16px #e8a04a88, 0 0 28px #e8702c33; }
}
.hostcorner .dot.warm { background: #e8a04a; box-shadow: 0 0 7px #e8a04a99; }
/* ------------------------------------------- the door, centred and blocky --
   EVERYTHING ON THIS SCREEN IS CENTRED except the log. A console log is read
   line by line and the eye needs a fixed left edge to come back to; centring
   it would make it unreadable, which is the one place on this screen where
   symmetry would cost something real.

   The type is squared off to sit under a pixel title: uppercase, wide letter
   spacing, and a monospace stack so every character occupies the same cell -
   which is what a bitmap font actually IS. It is not a true pixel font (that
   needs a full drawn alphabet, not a CSS rule) but it stops the proportional
   system font from fighting the wordmark above it. */
#login-server, #login-select, #login-create, .loginbox { text-align: center; }
.loginbox label { align-items: center; }
.loginbox input { text-align: center; }
#laninfo, #onlineinfo, #loginstatus, .rowhead, .serverhead, .muted { text-align: center; }
.servercard, .charcard { justify-content: center; text-align: center; }
/* the one thing that must NOT be centred */
#hostlog { text-align: left; font-family: ui-monospace, Consolas, 'Courier New', monospace; }

/* THE NAMES STAY AS THEY WERE. Squaring off the buttons and headings was
   right; doing it to the server and character names was not. Those are proper
   nouns - "Server 1", "This machine", somebody's character - and a name reads
   as a name in the ordinary face. Uppercased and monospaced it became another
   label, and the card lost the one thing on it you are actually choosing
   between. */
.loginbox button, .loginbox label, .rowhead, .serverhead, #loginstatus {
  font-family: ui-monospace, 'Courier New', monospace;
  text-transform: uppercase; letter-spacing: .10em;
}
/* A HAIRLINE THICKER, AND NO MORE. Jumping a whole font weight was too much -
   monospace goes from regular to bold with nothing in between, and bold looks
   shouty at this size. A 0.3px stroke in the text's own colour fattens every
   stem by a fraction of a pixel without changing the weight at all, which is
   the smallest notch there is. */
.loginbox button, .loginbox label, .rowhead, .serverhead, #loginstatus {
  -webkit-text-stroke: 0.3px currentColor;
}
/* and no thickening on them either - slim is what made them read as names */
.servercard b, .charcard .cname {
  font-family: inherit; text-transform: none; letter-spacing: normal;
  -webkit-text-stroke: 0;
}
.loginbox button { font-weight: 700; -webkit-text-stroke-width: 0.35px; }
/* the address reads as something you might have to type or read aloud, so it
   is the brightest thing in its row rather than a dim aside */
.shost, #onlineinfo .addr { color: #ffffff !important; }
.shost { font-family: ui-monospace, Consolas, monospace; letter-spacing: .02em; }
#btn-connect, #btn-play, #btn-create { letter-spacing: .18em; }
.loginbox button.link { letter-spacing: .06em; text-transform: uppercase; font-size: 10px; }
/* THE LIGHT LAYER. `screen` so it only ever ADDS - a dark pixel in the glow
   leaves the painting untouched and a bright one lifts it, which is what light
   does. Behind everything you can click, and it ignores the mouse entirely. */
.loginlight {
  position: absolute; inset: 0; width: 100%; height: 100%;
  mix-blend-mode: screen; pointer-events: none; z-index: 1;
}
/* THIS LINE PUT THE HOST BUTTONS BACK IN THE MIDDLE. Raising the panel above
   the light layer needed a z-index, and a z-index needs a position - but
   writing `position: relative` here overrode the `position: absolute` that
   pins the corner to the bottom right, and the two icons fell back into the
   flow. Only the panel needs relative; the corner is already positioned and
   just needs the layer number. */
.loginbox { position: relative; z-index: 2; }
.hostcorner { z-index: 60; }
/* the LAN line is an address somebody reads out to a friend - white, like the
   other one */
#laninfo, #laninfo * { color: #ffffff !important; }
#hostinfo { display: flex; flex-direction: column; gap: 4px; text-align: center; }
.hostcorner .dot { width: 7px; height: 7px; border-radius: 50%; background: #4a4436; }
.hostcorner .dot.on { background: #7fd08a; box-shadow: 0 0 7px #7fd08a99; }
/* PLAY IS THE POINT OF THIS SCREEN, so it sits in the middle of it and is
   wider than anything else - not a button in a stack of buttons. */
#btn-connect { display: block; margin: 12px auto 4px; min-width: 220px; padding: 12px 28px; font-size: 16px; }
#btn-play, #btn-create { display: block; margin: 10px auto 2px; min-width: 220px; }
.loginbox .rowhead, .serverhead { justify-content: center; gap: 10px; }
.rowhead { display: flex; justify-content: space-between; align-items: center; color: #7d7a8c; font-size: 12px; letter-spacing: 1px; text-transform: uppercase; }

#roster { display: flex; flex-direction: column; gap: 5px; max-height: 210px; overflow: auto; }
.charcard {
  display: flex; align-items: center; gap: 10px; padding: 6px 10px; cursor: pointer;
  background: #17161f; border: 1px solid #2b2937; border-radius: 6px;
}
.charcard:hover { border-color: #55506a; }
.charcard.on { border-color: #e8a04a; background: #201c19; }
.charcard canvas { width: 40px; height: 40px; image-rendering: pixelated; }
/* ============ A CARD IS THE CHARACTER'S OWN FACTS =====================
   Name and level on one line - level pushed to the right edge, where the eye
   finds it without reading the name first - and the vocation under them.
   The card is a column beside the portrait rather than one flat row, which
   is what makes room for any of this. */
.charcard .cwho { display: flex; align-items: baseline; gap: 8px; width: 100%; }
.charcard .cname { flex: 1; font-size: 14px; color: #ffffff; font-weight: 600; }
.charcard .clevel {
  font-size: 12px; color: #e8c14a; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.charcard .cvoc { font-size: 11.5px; color: #a2947c; letter-spacing: .02em; }
/* the portrait keeps its own column; everything written goes in the next */
.charcard { align-items: center; }
.charcard .cardtext { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.charcard.gone .cname { color: #8a8496; text-decoration: line-through; }
.charcard .del { opacity: 0; }
.charcard:hover .del { opacity: 1; }

/* ============ THE ACCOUNT STEPS (owner, 2026-10-05) ===================
   The same stone and gold as the rest of the door: the account box, the
   account's characters and the one-time "choose an account name" form. */
#login-account, #login-chars, #login-migrate { display: flex; flex-direction: column; gap: 8px; text-align: center; }
#acctroster { display: flex; flex-direction: column; gap: 5px; max-height: 210px; overflow: auto; }
#acctroster .charcard { background: #1a1611; border-color: #3c332a; }
#acctroster .charcard:hover { border-color: #6b5941; background: #221c15; }
#acctroster .charcard.on { border-color: #c07a2a; box-shadow: 0 0 0 1px #e8a04a44 inset; }
#acctroster .charcard .conline { font-size: 10.5px; color: #7fd08a; }
.loginbox .acctnote { font-size: 11.5px; line-height: 1.45; margin: 0 auto; max-width: 400px; }
.loginbox .acctnote b { color: #fff3d8; }
#login-account .checkrow { justify-content: center; margin: 0 auto; }
#btn-acctlogin, #btn-acctplay, #btn-migrate {
  display: block; margin: 8px auto 2px; min-width: 220px; letter-spacing: .18em;
  background: linear-gradient(#a8641f, #7a3a13);
  border: 1px solid #c07a2a; border-top-color: #e8b060; border-bottom-color: #5a2a0e;
  color: #fff3d8; font-weight: 600; text-shadow: 0 1px 0 #00000066;
}
#btn-acctlogin:hover, #btn-acctplay:hover, #btn-migrate:hover {
  background: linear-gradient(#c07726, #94481a); border-top-color: #ffd08a;
}
#btn-acctnew, #btn-newacct, #btn-oldchars, #btn-adminn {
  color: #f2e8d4; border-style: dashed; border-color: #6b5941;
  background: #241e17; font-size: 13px; padding: 9px;
}
#btn-oldchars, #btn-adminn { font-size: 12px; padding: 7px; }

/* ============ THE CHARACTER SELECT (owner, 2026-10-10) ==================
   "like in wow, diablo etc. You can see your character and spin him around."
   The account's characters step is a whole screen: a painted forest ledge
   over the vale, out of focus behind (the stage canvas, client/js/
   charselect.js), the picked one large on the ledge between two torches
   in the middle of what is left of the screen, the list in a stone panel on
   the right that never reaches the figure, and Enter the vale under it all.
   --cs-cx (the figure's centre) and --cs-panel (the list's width) are set by
   the layout in charselect.js. */
#login-chars:not(.hidden) {
  display: block; position: fixed; inset: 0; z-index: 5; gap: 0; text-align: left;
}
#login.charsel .loginbox > .sub, #login.charsel #hostinfo { display: none; }
#login.charsel .loginbox > .titlecanvas {
  position: fixed; z-index: 6; top: 18px; left: var(--cs-cx, 50%); transform: translateX(-50%);
  width: min(300px, calc(26vw / var(--uiz, 1))); margin: 0; pointer-events: none;
}
#login.charsel #loginstatus {
  position: fixed; z-index: 7; left: var(--cs-cx, 50%); bottom: 96px; transform: translateX(-50%);
  width: min(520px, calc(60vw / var(--uiz, 1)));
}
.cs-stage { position: absolute; inset: 0; width: 100%; height: 100%; image-rendering: pixelated; }
.cs-figure { position: absolute; cursor: grab; touch-action: none; }
.cs-figure.dragging { cursor: grabbing; }
.loginbox .cs-turn {
  position: absolute; width: 44px; height: 44px; padding: 0; border-radius: 50%;
  font-size: 15px; line-height: 1; color: #e8c14a; opacity: .72;
  background: #1a1612cc; border: 1px solid #4a3d2e; border-top-color: #6b5941; border-bottom-color: #221b14;
  box-shadow: 0 2px 10px #000a; -webkit-text-stroke: 0;
}
.loginbox .cs-turn:hover { opacity: 1; color: #ffd98a; border-color: #c07a2a; background: #2a2219; }
.cs-who {
  position: absolute; left: var(--cs-cx, 50%); transform: translateX(-50%);
  text-align: center; pointer-events: none; white-space: nowrap;
}
.cs-name {
  font-size: 32px; font-weight: 600; color: #ffe9b8; letter-spacing: .02em;
  text-shadow: 0 0 18px #e8702c66, 0 2px 4px #000, 0 0 10px #000c;
}
.cs-sub {
  margin-top: 4px; font-family: ui-monospace, 'Courier New', monospace; text-transform: uppercase;
  letter-spacing: .18em; font-size: 12.5px; color: #d9b878; -webkit-text-stroke: 0.3px currentColor;
  text-shadow: 0 1px 2px #000, 0 0 6px #000c;
}
.cs-online { margin-top: 6px; font-size: 12px; color: #7fd08a; text-shadow: 0 1px 2px #000; letter-spacing: .04em; }
.loginbox .cs-logout {
  position: absolute; top: 14px; left: 16px; padding: 8px 16px; font-size: 12px;
  background: #1a1612cc; border-radius: 4px;
}
.cs-panel {
  position: absolute; right: 22px; top: 64px; bottom: 92px; width: var(--cs-panel, 300px);
  display: flex; flex-direction: column; gap: 10px; padding: 14px 12px 12px;
  background: linear-gradient(#18140fee, #0e0c0aee);
  border: 1px solid #4a3d2e; border-top-color: #7a6545; border-bottom-color: #221b14; border-radius: 4px;
  box-shadow: 0 8px 30px #000c, inset 0 0 0 1px #e8a04a14, inset 0 1px 0 #e8b06022;
}
.cs-panel .rowhead { justify-content: space-between !important; padding: 0 4px 8px; border-bottom: 1px solid #3c332a; }
.cs-count { color: #c9a35a; font-size: 11px; letter-spacing: .1em; }
.cs-panel #acctroster { flex: 1; min-height: 0; max-height: none; gap: 6px; padding-right: 2px; }
#acctroster .charcard { justify-content: flex-start; text-align: left; padding: 7px 10px; border-radius: 3px; }
#acctroster .charcard canvas { width: 40px; height: 40px; flex: none; background: #0c0a08; border: 1px solid #2e2720; border-radius: 2px; }
#acctroster .charcard.on {
  background: linear-gradient(90deg, #3a2814, #1f1912 70%); border-color: #c07a2a;
  box-shadow: inset 3px 0 0 #e8b060, 0 0 12px #e8a04a22;
}
#acctroster .charcard.on .cname { color: #ffe0a0; }
.cs-panelbtns { display: flex; flex-direction: column; gap: 6px; }
.loginbox .cs-panelbtns button { margin: 0; }
.loginbox button.cs-del { color: #d99a88; border-color: #5a3328; background: #1e1512; font-size: 12px; padding: 7px; }
.loginbox button.cs-del:hover { color: #ffb8a2; border-color: #a8452a; background: #2a1712; }
.loginbox button:disabled { opacity: .45; cursor: default; }
#login-chars #btn-acctplay {
  position: absolute; left: var(--cs-cx, 50%); bottom: 28px; transform: translateX(-50%); margin: 0;
  min-width: 300px; padding: 15px 36px; font-size: 18px; letter-spacing: .22em;
  box-shadow: 0 4px 22px #000c, 0 0 26px #e8702c40;
}
#login-chars #btn-acctplay:focus-visible { outline: 2px solid #ffd98a; outline-offset: 3px; }
@media (max-height: 640px) {
  .cs-name { font-size: 24px; }
  #login-chars #btn-acctplay { padding: 11px 28px; font-size: 15px; bottom: 18px; }
  #login.charsel #loginstatus { bottom: 70px; }
}
#btn-acctnew:hover, #btn-newacct:hover, #btn-oldchars:hover, #btn-adminn:hover { color: #ffd98a; border-color: #c07a2a; background: #2e261c; }

/* ============ THE PASSWORD WINDOW ====================================
   Over everything, centred, and it does not touch the panel underneath -
   which is the entire point: selecting a locked character used to grow the
   login panel and shove the roster out from under a double-click. */
#passgate {
  /* ABOVE THE LOGIN SCREEN, AND THAT IS NOT A DETAIL. This was z-index 40
     and .overlay - the login screen itself - is 50, so the password window
     opened UNDERNEATH it: invisible, unclickable, and the game simply could
     not be entered by anybody whose character had a password. It has to
     outrank every layer it can ever appear over, and the login screen is the
     only one that matters here. */
  position: fixed; inset: 0; z-index: 900;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6,5,9,0.66);
}
#passgate.hidden { display: none; }
.passbox {
  width: min(340px, calc(88vw / var(--uiz, 1)));
  background: #1a1611; border: 1px solid #4a3d2e; border-radius: 8px;
  padding: 18px; box-shadow: 0 18px 50px rgba(0,0,0,0.6);
  display: flex; flex-direction: column; gap: 10px;
}
.passbox h2 { margin: 0; font-size: 17px; color: #ffffff; }
.passbox p { margin: 0; font-size: 12px; }
.passbox input[type="password"] {
  padding: 9px; background: #12100c; color: #f0e4cc;
  border: 1px solid #4a3d2e; border-radius: 4px; font-size: 14px;
}
.passbox input[type="password"]:focus { outline: none; border-color: #c07a2a; }
.passbox .row { display: flex; gap: 8px; }
.passbox .row button { flex: 1; padding: 9px; font-size: 13px; }
#btn-passok {
  background: linear-gradient(#a8641f, #7a3a13);
  border: 1px solid #c07a2a; border-top-color: #e8b060; border-bottom-color: #5a2a0e;
  color: #fff3d8; font-weight: 600;
}
#btn-passok:hover { background: linear-gradient(#c07726, #94481a); }
#btn-passcancel {
  background: transparent; border: 1px solid #4a3d2e; color: #a2947c;
}
#btn-passcancel:hover { color: #f0e4cc; border-color: #6b5941; }
.passbox .checkrow { display: flex; align-items: center; gap: 7px; font-size: 12px; color: #a2947c; }
.passbox .checkrow input { width: auto; }

.createrow { display: flex; gap: 14px; align-items: flex-start; }
.previewbox { display: flex; flex-direction: column; gap: 6px; align-items: center; }
#lookpreview {
  image-rendering: pixelated; background: #16151d;
  border: 1px solid #2b2937; border-radius: 6px;
}
#lookopts { flex: 1; display: flex; flex-direction: column; gap: 7px; }
.lookrow { display: flex; align-items: center; gap: 8px; }
.lookname { width: 58px; font-size: 11px; color: #8b8898; }
.swatches { display: flex; gap: 4px; flex-wrap: wrap; }
.swatch { width: 22px; height: 22px; padding: 0; border-radius: 4px; border: 2px solid #2b2937; }
.swatch.on { border-color: #fff; }
.stylebtn { font-size: 11px; padding: 4px 8px; }
.stylebtn.on { background: #7a3a1a; border-color: #a8542a; }
.hostrow { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.dot { width: 10px; height: 10px; border-radius: 50%; background: #4a4756; }
.dot.on { background: #5ec86a; box-shadow: 0 0 8px #5ec86a; }
#hostlog {
  max-height: 130px; overflow: auto; background: #0f0e14; border: 1px solid #26242f;
  padding: 6px; font-size: 10px; color: #7d8a7d; margin: 0; white-space: pre-wrap;
}
#death h1 { color: #d0455a; letter-spacing: 6px; }

.modal {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 520px; max-height: calc(76vh / var(--uiz, 1)); overflow: auto; z-index: 60;
  background: #16151d; border: 1px solid #3a3748; border-radius: 8px; padding: 18px;
  box-shadow: 0 20px 60px #000c;
}
.modal h2 {
  margin: -18px -18px 10px; padding: 12px 18px; color: #e8a04a; font-size: 18px;
  cursor: move; user-select: none; background: #1b1a24; border-bottom: 1px solid #2e2b3a;
  border-radius: 8px 8px 0 0;
}
.npcline { color: #c9c6d6; font-style: italic; margin: 4px 0; }
.qoffer { border: 1px solid #2e2b3a; border-radius: 6px; padding: 10px; margin: 10px 0; background: #1b1a24; }
.qoffer b { color: #ffe08a; }
/* a quest ready to hand in: a golden border and a small "?" in the corner,
   the same yellow as the "?" over the villager's head (owner, 2026-10-05) */
.qoffer.ready { position: relative; border: 2px solid #e8b83a; box-shadow: 0 0 10px #e8b83a40, inset 0 0 0 1px #ffd21f33; }
.qoffer.ready::after {
  content: '?'; position: absolute; top: 4px; right: 8px;
  font: bold 15px "Segoe UI", sans-serif; color: #ffd21f; text-shadow: 0 1px 2px #000c;
}
.qoffer p { color: #b6b3c4; margin: 6px 0; }
.rew { color: #7fd08a; font-size: 12px; margin: 6px 0; }
/* a price you cannot meet is not a reward - same line, the refusal colour */
.rew.no { color: #d0455a; }
.ablist { max-height: calc(46vh / var(--uiz, 1)); overflow: auto; display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.abrow { padding: 8px; background: #1b1a24; border: 1px solid #2e2b3a; border-radius: 5px; cursor: pointer; display: flex; flex-direction: column; gap: 2px; }
.abrow:hover { border-color: #6a6480; }
.abrow.dim { opacity: .4; }
.abrow span { color: #a5a2b4; font-size: 12px; }
.abrow .meta { color: #7d7a8c; font-size: 11px; }
/* an ability's "Base damage" (shared/abdamage.js, owner 2026-10-11) - a stat line under its words */
.abrow .abdmg, .qoffer .abdmg { display: block; font-style: normal; color: #e8b47a; font-size: 11.5px; margin-top: 2px; }

/* ----------------------------------------------------------------- HUD */
#hud { position: absolute; inset: 0; pointer-events: none; }
#hud > * { pointer-events: auto; }
/* THE TOP LEFT IS TWO COLUMNS NOW. Your plate, and the cat's beside it -
   which is where a dependant belongs: in your eyeline while you are reading
   your own bars, not underneath them where it pushes everything else down
   the screen. The column is only as wide as it needs to be, and the whole
   thing collapses back to one plate when you have no cat. */
#topleft { position: absolute; left: 12px; top: 12px; width: 480px; }
#plates { display: flex; align-items: flex-start; gap: 6px; }
#plates #vitals { width: 250px; flex: none; }
#vitals { background: #12111ac0; border: 1px solid #2b2937; border-radius: 6px; padding: 8px; }
.bar { position: relative; height: 16px; background: #0c0b10; border: 1px solid #2b2937; border-radius: 3px; margin-bottom: 4px; overflow: hidden; }
.bar i { display: block; height: 100%; transition: width .18s; }
.bar span { position: absolute; inset: 0; text-align: center; font-size: 10px; line-height: 16px; text-shadow: 0 1px 2px #000; }
.bar.hp i { background: linear-gradient(#d0455a, #8a2036); }
.bar.mp i { background: linear-gradient(#4a7ad0, #23407a); }
.bar.st { height: 6px; }
.bar.st i { background: linear-gradient(#c9b46a, #8a7530); }
/* ============ HOW FED YOU ARE ===================================
   The fourth bar on the plate, under stamina, in the same furniture as
   the other three - because that is what it is. It was a floating pixel
   instrument at the top left, which is directly on top of this plate:
   a new system drawn over an old one, and it looked exactly as odd as
   that sounds.

   Shorter than health and mana. A meal is not as urgent as your blood
   is, and a row of four full-height bars reads as four equally important
   things. The colour is the warning: warm while there is food in you,
   amber as it goes, and red once it is empty and costing you health. */
.bar.food { height: 11px; }
/* DARK TEXT ON A GOLD BAR. The other bars are light text on red and blue and
   that is right for them; the same light text on this one sat on gold and
   nearly vanished. It goes light again the moment the bar turns red. */
.bar.food span { font-size: 9px; line-height: 11px; color: #2a1d06;
  text-shadow: 0 1px 0 rgba(255,255,255,.22); font-weight: 600; }
.bar.food.out span { color: #f0dcd4; text-shadow: 0 1px 2px #000; font-weight: 400; }
.bar.food i { background: linear-gradient(#c9922f, #7a5714); }
.bar.food.low i { background: linear-gradient(#d8a03c, #8a5c14); }
.bar.food.out { border-color: #6a2a24; }
.bar.food.out i { background: linear-gradient(#d0455a, #8a2036); }
/* and once it is actually killing you, the bar itself breathes - the one
   thing on the plate that moves when nothing is happening */
.bar.food.dying { animation: fedpulse 1.2s ease-in-out infinite; }
@keyframes fedpulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(208,69,90,0); }
  50% { box-shadow: 0 0 6px 1px rgba(208,69,90,.55); }
}
@media (prefers-reduced-motion: reduce) { .bar.food.dying { animation: none; } }
.line2 { display: flex; justify-content: space-between; font-size: 11px; color: #a5a2b4; margin-top: 6px; }
.mainskillrow { display: flex; align-items: center; gap: 6px; margin-top: 5px; font-size: 11px; color: #c9c6d6; }
.xpbar { flex: 1; height: 5px; background: #0c0b10; border-radius: 3px; overflow: hidden; }
.xpbar i { display: block; height: 100%; background: #e8a04a; }
/* under the tracker, and it only ever grows downward from there */
#xpfeed { margin-top: 8px; display: flex; flex-direction: column; gap: 2px;
          align-items: flex-start; }
.xpfloat { font-size: 11px; color: #ffe08a; animation: fadeup 2.2s forwards;
           white-space: nowrap; }
@keyframes fadeup { 0% { opacity: 1; } 80% { opacity: .95; } 100% { opacity: 0; transform: translateY(-8px); } }
#tracker { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.trk { background: #12111ab0; border-left: 2px solid #e8a04a; padding: 5px 8px; font-size: 11px; }
.trk b { display: block; color: #ffe08a; margin-bottom: 2px; }
.trk span { display: block; color: #a5a2b4; }
.trk span.done { color: #5ec86a; text-decoration: line-through; }
/* ============ THE MAIN QUEST WEARS A STAR =============================
   An errand and the story looked identical in a list. The star is gold and
   sits at the end of the line rather than in front of it, so a column of
   quests still reads down the left edge and the important one is marked
   without being shouted. `display: inline` on the tracker's b overrides the
   block above just enough to let the star sit beside the name. */
/* the reward list: a label column and a value column, so three different
   kinds of payment line up instead of running together in a sentence */
.rew { margin-top: 8px; padding: 7px 9px; background: #16151d; border: 1px solid #2b2937;
  border-radius: 4px; font-size: 11px; }
.rew b { display: block; color: #ffe08a; font-size: 11px; letter-spacing: .3px;
  text-transform: uppercase; margin-bottom: 5px; }
.rewrow { display: grid; grid-template-columns: 84px 1fr; gap: 8px; padding: 2px 0;
  align-items: baseline; }
.rewrow i { color: #7d7a8c; font-style: normal; }
.rewrow em { color: #d8d4e4; font-style: normal; }
/* THE LEVEL FIRST (owner, 2026-09-29: "All quests should reward regular
   experience as well") - the character experience a quest pays, in the
   level bar's own gold, and the plain words for how much of a level it is.
   Inline, not the list heading's block capitals it would otherwise inherit. */
.rewrow b.rewlv { display: inline; margin: 0; font-size: 12px; letter-spacing: 0;
  text-transform: none; color: #ffe08a; text-shadow: 0 0 6px #f0b95455; }
.rewrow .rewlvwhy { color: #7d7a8c; }
/* A QUEST IS WORTH A SKILL POINT (CONFIG.SKILL_TREE2) - the star-chart's starlight */
.rewrow b.rewsp { display: inline; margin: 0; font-size: 12px; letter-spacing: 0;
  text-transform: none; color: #fff6d8; text-shadow: 0 0 6px #e8c46a77; }
.qrxprow.qrsp i, .qrxprow.qrsp b { color: #e8c46a; }
/* and the same gold on the notice board, where a contract pays it too */
em.boardlv { font-style: normal; color: #ffe08a; }
.boardlvnote { font-size: 11px; margin: 2px 0 6px; }
/* today / this week / all time, on both lists */
.spantabs { display: flex; gap: 4px; margin: 4px 0 8px; }
.spantab { background: #1b1a24; border: 1px solid #2e2b3a; color: #8b8898;
  padding: 3px 10px; font-size: 11px; border-radius: 3px; cursor: pointer; }
.spantab:hover { border-color: #4a4658; color: #c8c6d4; }
.spantab.on { background: #2a2418; border-color: #e8a04a; color: #ffe08a; }
/* who is online: a level, a name, and for the admin where they are standing */
.whotab { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; min-width: 220px; }
.whorow { display: flex; align-items: baseline; gap: 8px; padding: 3px 6px;
  background: #16151d; border: 1px solid #23212c; border-radius: 3px; font-size: 12px; }
.whorow.me { border-color: #4a4230; background: #1d1b16; }
.whorow .wlv { color: #7d7a8c; font-size: 11px; min-width: 38px; font-variant-numeric: tabular-nums; }
.whorow .wname { color: #e6e2f0; flex: 1; }
.whorow .wwhere { color: #8fb8e0; font-size: 11px; }
.whorow .wat { color: #6a6478; font-size: 10px; font-variant-numeric: tabular-nums; }
/* ============ FRIENDS, AND A LOOK AT WHAT THEY WEAR ======================
   The Friends window is the Online list's rows with a lamp in front: lit for
   on, dark for off. The inspect window is small on purpose - owner: "a little
   window" - the paper doll at a smaller size and one line over it. */
.panel[data-name="friends"] { width: 330px; }
.friendadd { display: flex; gap: 6px; margin: 6px 0 4px; }
.friendadd input { flex: 1; min-width: 0; background: #0f0e14; border: 1px solid #2b2937; color: #e6e2f0;
  padding: 4px 6px; border-radius: 3px; font-size: 12px; }
.friendadd input:focus { border-color: #6a6480; outline: none; }
.friendtab { max-height: 360px; overflow-y: auto; }
.friendrow { align-items: center; }
.friendrow .fdot { width: 7px; height: 7px; border-radius: 50%; background: #3a3746; flex: none; }
.friendrow.on .fdot { background: #7bc86c; box-shadow: 0 0 5px #7bc86c99; }
.friendrow .wname { color: #8b8898; }
.friendrow.on .wname { color: #e6e2f0; }
.friendrow .fwhere { color: #6a6478; font-size: 11px; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.friendrow.on .fwhere { color: #8fb8e0; }
.friendrow button.link { padding: 0 2px; font-size: 11px; }
.friendrow button.fx { color: #8b6a6a; }
.panel[data-name="inspect"] { width: max-content; }
.panel[data-name="inspect"] h3 { white-space: nowrap; }
.insplv { color: #a5a2b4; font-weight: 400; font-size: 12px; margin-left: 4px; }
.inspgrid { display: grid; grid-template-columns: repeat(6, 38px); grid-template-rows: repeat(4, 38px);
  gap: 3px; width: max-content; margin: 2px auto 4px; }
.inspgrid .slot { grid-area: var(--at); cursor: default; }
.inspgrid .slot.eq.toolslot { margin-right: 0; box-shadow: none; }
.inspgrid .slot .slotname { font-size: 8px; }
/* at this size the word would sit on the picture - a worn slot shows the piece, an empty one says what goes there */
.inspgrid .slot.filled .slotname { display: none; }
.mainstar { color: #ffd24a; font-size: 12px; text-shadow: 0 0 4px #ffd24a70; }
.questrow .mainstar { float: right; margin-left: 6px; }
.trk.main { border-left-color: #ffd24a; }
.trk.main b .mainstar { float: right; }
/* ============ ONE AT A TIME, IF YOU LIKE ==============================
   Twelve quests on the tracker is a wall of text down the side of the screen,
   and eleven of them are things you are not doing this hour. Each folds to
   its own title, so the two you are actually working stay open and the rest
   are one line each.

   The whole title row is the hit target - a 9px chevron is a thing you hunt
   for - and the chevron itself is only there to say which way it will go. */
.trk .trkhead { display: flex; align-items: baseline; gap: 6px; cursor: pointer; }
.trk .trkhead:hover b { color: #fff0b8; }
.trk .trkhead > b { flex: 1 1 auto; margin-bottom: 2px; }
.trk .trkfold {
  flex: 0 0 auto; color: #6f6a80; font-size: 10px; line-height: 1;
  padding: 1px 3px; user-select: none;
}
.trk .trkhead:hover .trkfold { color: #e8a04a; }
.trk.min .trkfold { color: #8a8299; }
/* only the step lines fold away - the chevron and the marks beside the name
   are spans too, and a plain descendant selector hid them with the steps, so
   a folded quest lost the arrow that says it will open again */
.trk.min > span { display: none; }
/* folded, it is a title and nothing else - so a column of them reads as a
   list rather than as a stack of empty boxes */
.trk.min { padding-top: 3px; padding-bottom: 3px; opacity: 0.82; }
.trk.min .trkhead > b { margin-bottom: 0; }
/* the fold-everything button above the list: quiet until it is wanted, the
   width of its words rather than the column, so it reads as a control and
   not as another quest */
.trkbar { display: flex; }
.trkall {
  background: #12111ab0; border: 1px solid #2e2b3a; border-radius: 3px;
  color: #8b8898; font: inherit; font-size: 10px; letter-spacing: .3px;
  padding: 2px 8px; cursor: pointer;
}
.trkall:hover { border-color: #e8a04a; color: #ffe08a; }
.trkall:focus-visible { outline: 1px solid #e8a04a; outline-offset: 1px; }
/* the team-quest mark: three heads in the party's blue, see partyBadge() */
.teambadge, .trk .teambadge {
  display: inline-flex; align-items: center; gap: 3px; vertical-align: -1px;
  color: #7fc4ff; font-weight: normal; cursor: help;
}
.teambadge svg { display: block; filter: drop-shadow(0 0 2px #7fc4ff55); }
.teambadge i { font-style: normal; font-size: 10px; letter-spacing: .4px;
  text-transform: uppercase; }
/* the one-off choice of reward: three cards, nothing chosen until clicked */
.pickrow { display: flex; gap: 8px; margin: 10px 0; flex-wrap: wrap; }
.pickcard {
  flex: 1 1 150px; min-width: 140px; display: flex; flex-direction: column;
  align-items: center; gap: 4px; padding: 10px 8px; cursor: pointer;
  background: #1b1a24; border: 1px solid #3a3748; border-radius: 5px;
  text-align: center;
}
.pickcard:hover { border-color: #e8a04a; background: #241f2e; }
.pickcard img { width: 40px; height: 40px; image-rendering: pixelated; }
.pickcard b { color: #ffe08a; font-size: 12px; }
.pickcard .meta { color: #a5a2b4; font-size: 11px; }
.pickcard .flavour { color: #7d7a8c; font-size: 10px; font-style: italic; margin-top: 3px; }
/* choose, then take (ui.js pickChooser): a click selects, the button takes */
.pickcard.picked { border-color: #ffd34a; background: #2c2533; box-shadow: 0 0 0 1px #ffd34a, 0 0 10px #ffd34a55; }
.pickwrap.pickarming .pickcard, .pickwrap.pickarming .pickgo { opacity: .55; cursor: wait; }
.pickinfo { margin: 2px 0 8px; font-size: 12px; max-height: 24vh; overflow-y: auto; }
.itemtip.pickinfo { position: static; max-width: none; pointer-events: auto; box-shadow: none; z-index: auto; }
.pickgo { display: block; width: 100%; margin: 4px 0 6px; padding: 6px 10px; font-weight: 600; }
.pickgo:not(:disabled) { background: #6b4a16; border-color: #ffd34a; color: #fff3cf; }

#sidebuttons { position: absolute; right: 12px; top: 200px; display: flex; flex-direction: column; gap: 5px; }
/* The longest word on the rail is "Quests", and at forty-four pixels it was
   touching both edges - the column read as cramped rather than as tidy. Ten
   more pixels and a little breathing room inside is enough for every label
   with room to spare, and the rail is still a rail. */
#sidebuttons button { width: 54px; height: 34px; font-size: 11px;
  padding: 0 4px; white-space: nowrap; }
/* ============ THE SIDE BUTTONS, AS PICTURES ===========================
   Owner, 2026-10-05: "I would like the player to be able to switch between
   B & C for the buttons. B should be "expanded mode" / C should be
   "Compacted mode"". client/js/sidebar.js dresses the same buttons and puts
   .sbnew plus .sbx (expanded) or .sbc (compacted) on #sidebuttons - and
   only while CONFIG.SIDEBAR_ICONS_OPEN is on, so with it off nothing below
   matches and the column above is what you get. The looks are the mockups'
   (docs/sidebar-mockups.html). */
#sidebuttons.sbnew { gap: 0; overflow: hidden; z-index: 3; }
#sidebuttons.sbnew.sbscroll { overflow-y: auto; scrollbar-width: thin; scrollbar-color: #3a3748 transparent; }
#sidebuttons.sbnew > button { position: relative; margin: 0; flex: none; font-family: inherit; white-space: nowrap; }
#sidebuttons.sbnew .sbico { display: block; image-rendering: pixelated; flex: none; pointer-events: none; }
#sidebuttons.sbnew .sbw { font-style: normal; pointer-events: none; }
#sidebuttons.sbnew .sbbadge { min-width: 14px; height: 14px; padding: 0 3px; border-radius: 7px;
  background: #d8332a; color: #fff; font: 700 10px/14px "Segoe UI", sans-serif; text-align: center;
  border: 1px solid #1c1418; box-shadow: 0 0 0 1px #ff8a7a55, 0 1px 2px #000a; z-index: 3; pointer-events: none; }
#sidebuttons.sbnew .sbbadge[hidden], #sidebuttons.sbnew .sbk[hidden] { display: none; }
/* the switch: a thin strip on top of the menu, a chevron pointing the way it folds */
#sidebuttons.sbnew #sbtoggle { height: 14px; padding: 0; margin: 0; flex: none; display: flex; align-items: center; justify-content: center;
  font: 700 12px/1 "Segoe UI", sans-serif; color: #8b8898; background: #17161ff2;
  border: 0; border-left: 1px solid #3a3748; border-top: 1px solid #e8a04a55; border-radius: 6px 0 0 0; }
#sidebuttons.sbnew #sbtoggle:hover { color: #ffe08a; background: #2a2433f5; }
/* ...and it stays on top while a short window scrolls the column: it used to
   scroll away with the buttons, and with it the only way back from the mode
   you had just picked short of the Escape menu (owner, 2026-10-11) */
#sidebuttons.sbnew #sbtoggle { position: sticky; top: 0; z-index: 4; }
#sidebuttons.sbnew.sbscroll #sbtoggle { background: #17161f; }
#sidebuttons.sbnew.sbscroll { scroll-padding-top: 16px; }     /* a button scrolled to is never left under it */
#sidebuttons.sbnew.sbscroll #sbtoggle:hover { background: #2a2433; }

/* ---- B, expanded: slim tabs hanging off the edge ---- */
/* 96 wide so the open one's bookmark has room; the column itself is 82,
   and the 14 to its left lets clicks through to the world */
#sidebuttons.sbx { right: 0; top: 200px; width: 96px; align-items: flex-end; pointer-events: none; }
#sidebuttons.sbx > button, #sidebuttons.sbx #sbtoggle { pointer-events: auto; }
#sidebuttons.sbx #sbtoggle { width: 82px; }
#sidebuttons.sbx > button[data-sb] { width: 82px; height: var(--sbrow, 37px); display: flex; align-items: center; gap: 5px;
  padding: 0 0 0 5px; border: 0; border-radius: 0; text-align: left;
  background: linear-gradient(90deg, #1f1d29f2, #17161ff2); border-left: 1px solid #3a3748;
  border-bottom: 1px solid #e8a04a38; }
#sidebuttons.sbx > button[data-sb]:last-child { border-bottom-left-radius: 6px; border-bottom-color: #e8a04a55; }
#sidebuttons.sbx .sbic, #sidebuttons.sbx .sbk { display: none; }
#sidebuttons.sbx .sbib { width: 32px; height: 28px; }
#sidebuttons.sbx.sbsmall .sbib { width: 16px; height: 14px; }     /* exactly half: still crisp */
#sidebuttons.sbx .sbw { font: 600 11px/1 "Segoe UI", sans-serif; color: #cfc8dc; }
#sidebuttons.sbx .sbw.sbwlong { font-size: 10px; letter-spacing: -0.3px; white-space: nowrap; }
#sidebuttons.sbx > button[data-sb]:hover { background: linear-gradient(90deg, #322b3cf5, #221f2bf2); box-shadow: inset 2px 0 0 #e8a04a; }
#sidebuttons.sbx > button[data-sb]:hover .sbw { color: #ffe08a; }
/* the open one: a bookmark that sticks out of the stack */
#sidebuttons.sbx > button[data-sb].on { width: 96px; padding-left: 8px; z-index: 2;
  background: linear-gradient(180deg, #f0d49a, #d8b06a 60%, #c49650);
  border: 1px solid #1c1418; border-right: none; border-radius: 7px 0 0 7px;
  box-shadow: inset 0 1px 0 #fff4cc, inset 0 -2px 0 #9a6e34, -3px 2px 6px #000b; }
#sidebuttons.sbx > button[data-sb].on .sbw { color: #3a2718; font-weight: 700; }
#sidebuttons.sbx > button[data-sb].on::after { content: ""; position: absolute; left: -1px; top: 50%; width: 3px; height: 14px; margin-top: -7px;
  background: #e8a04a; border-radius: 2px; box-shadow: 0 0 6px #ffd46a; }
#sidebuttons.sbx .sbbadge { position: static; margin-left: auto; margin-right: 5px; }

/* ---- C, compacted: a two-wide block of pictures ---- */
#sidebuttons.sbc { right: 2px; top: 200px; display: grid; grid-template-columns: 36px 36px; gap: 3px; align-content: start;
  padding: 4px; border-radius: 6px; background: #0d0c10a8; box-shadow: 0 0 0 1px #3a374888; }
#sidebuttons.sbc #sbtoggle { grid-column: 1 / -1; width: auto; border-radius: 4px; border: 1px solid #3a3748; }
#sidebuttons.sbc > button[data-sb] { width: 36px; height: 36px; padding: 0; border-radius: 4px;
  background: linear-gradient(160deg, #2c2936, #1d1b25); border: 1px solid #3a3748;
  box-shadow: inset 1px 1px 0 #ffffff14, inset -1px -1px 0 #0007; }
#sidebuttons.sbc .sbib, #sidebuttons.sbc .sbw { display: none; }
#sidebuttons.sbc .sbic { position: absolute; left: 1px; top: 1px; width: 32px; height: 32px; }
#sidebuttons.sbc .sbk { position: absolute; right: 1px; bottom: 1px; font: 700 8px/9px "Segoe UI", sans-serif; color: #ffe08a;
  background: #0d0c10d9; border-radius: 2px; padding: 0 2px; min-width: 8px; text-align: center; pointer-events: none; }
#sidebuttons.sbc > button[data-sb]:hover { border-color: #e8a04a; background: linear-gradient(160deg, #3a3346, #25212e);
  box-shadow: inset 1px 1px 0 #ffffff1f, 0 0 6px #e8a04a88; }
#sidebuttons.sbc > button[data-sb].on { border-color: #ffd46a; background: radial-gradient(circle at 50% 45%, #6a4a20, #2a2116 75%);
  box-shadow: inset 0 0 0 1px #e8a04a, 0 0 8px #e8a04acc; }
#sidebuttons.sbc .sbbadge { position: absolute; right: -4px; top: -4px; }
/* C's name tag, out to the left of the block (lives on body so a scrolling
   column cannot clip it) */
#sbtag { position: fixed; z-index: 60; pointer-events: none; white-space: nowrap;
  display: flex; align-items: center; gap: 6px; padding: 4px 6px 4px 9px; border-radius: 4px;
  background: #1b1a24; border: 1px solid #e8a04a; color: #ffe08a; font: 600 12px/1 "Segoe UI", sans-serif;
  box-shadow: 0 2px 8px #000c; }
#sbtag[hidden] { display: none; }
#sbtag::after { content: ""; position: absolute; right: -6px; top: 50%; margin-top: -5px;
  border: 5px solid transparent; border-left-color: #e8a04a; border-right: 0; }
#sbtag kbd { font: 700 10px/1 "Segoe UI", sans-serif; color: #1c1418; background: #ffe08a;
  border-radius: 3px; padding: 2px 4px; box-shadow: inset 0 -1px 0 #b08a3a; }
/* what makes room for it: the battle list ("In range") sat 18 px under B, so
   while it is in its home spot it moves out past the menu; one you dragged
   stays where you put it. The corner bar steps left of B's long column,
   and of C's only when a short window makes the two meet (sb-push). */
body.sb-x #battlebox:not([data-moved]) { right: 100px !important; }
body.sb-c #battlebox:not([data-moved]) { right: 94px !important; }
body.sb-x #hotbar3, body.sb-c.sb-push #hotbar3 { right: 102px; }

/* character level row */
.charrow { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; font-size: 11px; }
.charrow .lv { color: #c9c6d6; }
.charrow .lv b { color: #ffe08a; font-size: 13px; }
.xpbar.wide { flex: 1; height: 7px; }
.xpbar.wide i { background: linear-gradient(90deg, #e8a04a, #ffe08a); }
#vocinfo { color: #ffe08a; cursor: pointer; }
.pill { display: inline-block; margin-top: 6px; padding: 3px 9px; border-radius: 10px; font-size: 11px; background: #3a2c14; border: 1px solid #e8a04a; color: #ffe08a; cursor: pointer; }
.pill.flash { animation: pillflash 1s ease-in-out 4; }
@keyframes pillflash {
  0%, 100% { box-shadow: 0 0 0 #e8a04a00; background: #3a2c14; }
  50% { box-shadow: 0 0 14px #e8a04aaa; background: #5a4420; }
}
.pill.protect { background: #14301c; border-color: #5ec86a; color: #a8e8b0; cursor: default; }
/* the two stances - warm for holding ground, cool for keeping your feet, so
   which one is up is readable at a glance without reading the words */
.pill.brace { background: #2e1f14; border-color: #c07a3a; color: #f0c088; cursor: default; }
.pill.light { background: #14222e; border-color: #4a9ac0; color: #9fd8ea; cursor: default; }
#deathnote { max-width: 460px; text-align: center; line-height: 1.6; }
.pill.safe { background: #14301c; border-color: #5ec86a; color: #a8e8b0; cursor: default; }
/* CHASE. The one pill on the bar that is a switch rather than a readout, so
   it keeps the default pill's pointer and its hover - and it is coloured by
   STATE, not by category: green while it will run you in, red while it will
   not. Being able to read which at a glance matters more here than matching
   the other pills, because it changes what the next click does. */
.pill.chase { cursor: pointer; }
.pill.chase.on { background: #14301c; border-color: #5ec86a; color: #a8e8b0; }
.pill.chase.off { background: #301618; border-color: #a04a4a; color: #e8a0a0; }
.pill.chase:hover { filter: brightness(1.25); }
/* who you are walking after - click it to stop (FOLLOW in game.js) */
.pill.follow { display: block; width: max-content; background: #14222e; border-color: #4a9ac0; color: #9fd8ea; cursor: pointer; }
.pill.follow.hidden { display: none; }
.pill.follow:hover { filter: brightness(1.25); }

/* both bars grew, so they are centred in the room LEFT of the chat box
   rather than on the window - ten slots centred on the window buried the
   first two behind the chat log */
/* hemmed in on every side - it throbs, the way the sound does */
.pill.penned { background: #3a1418; border-color: #8a2c34; color: #ff9a9a; animation: penned 1s ease-in-out infinite; }
@keyframes penned { 0%,100% { opacity: 1; } 50% { opacity: .55; } }

/* ============ THE CLOCK SITS BESIDE THE MINIMAP =======================
   It was dead centre at the top, which is the busiest strip of the screen
   and - since the target frame arrived - somewhere it now had to share with
   the health bar of whatever you are fighting.

   The minimap owns the top right corner: 168 across with 12 of padding, so
   its left edge is 180 in from the right. The clock is parked just clear of
   that, which puts the two things you glance at for orientation - where you
   are, and what hour it is - side by side rather than at opposite ends of
   the screen. See minimapRect() in render.js, which is where those two
   numbers come from. */
/* THE FRAME IS THE BOX NOW. This used to be a rounded plate with a border
   and a background, because the clock was two lines of text. The face draws
   its own housing, so a second box around it would be a frame around a
   frame - all that is left here is where it goes. */
#clock { position: absolute; right: 190px; top: 12px; pointer-events: none; width: 96px; }
/* ============ THE AIR GAUGE ======================================
   Under the clock, same width, same corner. It is only ever on screen
   inside a dig site, so it costs nothing anywhere else. */
#airgauge { position: absolute; right: 190px; top: 112px; pointer-events: none; width: 96px; }

}
#airgauge.hidden { display: none; }
.agface { width: 100%; height: auto; display: block; image-rendering: pixelated; }
/* the needle. The transition is what makes a canister READ as ten
   minutes: the needle sweeps up over half a second instead of jumping,
   which is the whole difference between a number changing and something
   happening. --air is set by UI.digClock. */
.ag-needle { transform-origin: 23.5px 21px; transform: rotate(var(--air, 110deg));
  transition: transform .55s cubic-bezier(.34,1.3,.64,1); }
.ag-time { font: 700 6px "Segoe UI", sans-serif; fill: #e8dcc8; text-anchor: middle; }
/* bubbles, drifting up the glass. They are the reason it is worth
   glancing at when nothing is happening. */
.ag-bub { animation: agbub 3.4s linear infinite; opacity: .8; }
.ag-bub2 { animation-duration: 4.6s; animation-delay: -1.7s; }
.ag-bub3 { animation-duration: 2.8s; animation-delay: -0.9s; }
@keyframes agbub {
  0%   { transform: translateY(4px);  opacity: 0; }
  15%  { opacity: .85; }
  85%  { opacity: .5; }
  100% { transform: translateY(-15px); opacity: 0; }
}
/* ============ AND WHAT IT DOES WHEN THE AIR IS GOING =============
   Low is amber and the bubbles slow down. Under a minute the whole
   instrument shakes and the plate goes red - a thing you notice out of
   the corner of your eye while you are looking at a wall. */
#airgauge.low .ag-time { fill: #e8c14a; }
#airgauge.low .ag-bub { animation-duration: 6s; }
#airgauge.out .ag-time { fill: #ff8a6a; }
#airgauge.out .agface { animation: agshake 1.9s ease-in-out infinite; }
#airgauge.out .ag-bub { display: none; }
/* ============ A SHIVER, NOT A RATTLE ==============================
   The first one jumped a whole pixel every quarter second and read as a
   broken instrument. This is the needle-and-glass equivalent of somebody
   catching their breath: nothing at all for a second and a half, then a
   small quick tremble, then still again. Half a pixel is enough - it is
   in the corner of your eye, and a thing you notice out of the corner of
   your eye does not need to be big, it needs to be WRONG. */
@keyframes agshake {
  0%, 68%, 100% { transform: translate(0, 0) rotate(0deg); }
  72% { transform: translate(-0.5px, 0.5px) rotate(-0.4deg); }
  76% { transform: translate(0.5px, -0.5px) rotate(0.4deg); }
  80% { transform: translate(-0.4px, 0.3px) rotate(-0.3deg); }
  84% { transform: translate(0.3px, -0.2px) rotate(0.2deg); }
  88% { transform: translate(0, 0) rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .ag-bub, #airgauge.out .agface { animation: none; }
  .ag-needle { transition: none; }
}
.clockface { width: 100%; height: auto; display: block; image-rendering: pixelated; }
/* the two turning layers. --tod is set from the server's time of day, so
   both read the same number and cannot come apart. */
.cf-turn { transform: rotate(var(--tod, 0deg)); transform-origin: 23.5px 22px; }
.cf-time {
  fill: #e8d9b8; font: 700 7px "Segoe UI", system-ui, sans-serif;
  text-anchor: middle; letter-spacing: .4px;
}
.cf-phase {
  fill: #7d7a8c; font: 700 3.4px "Segoe UI", system-ui, sans-serif;
  text-anchor: middle; letter-spacing: 1px; text-transform: uppercase;
}
#clock.night .cf-time { fill: #a9c4e8; }
#clock.dusk .cf-time, #clock.dawn .cf-time { fill: #e8a04a; }

/* THE TWO BARS ARE CENTRED ON THE SCREEN, not on what is left over.
   They ran from left:364 to right:12, so their middle sat 176 pixels right of
   the middle of the window - measured, not guessed - and the right-hand end
   ran under the side buttons. Anchored to both edges they centre truly, and
   the chat has been lifted clear so nothing is drawn over anything. */
/* ============ THE HEAL, ON ITS OWN SHELF ================================
   One row north of the weapon keys: 66px is the weapon bar, its slots are
   40 tall, so 112 clears them with a hair of air between. Centred on the
   same axis as everything else in the row so it reads as part of the stack
   rather than as something floating loose over the world. */
/* ============ ESTABLISHING WORLD ==================================
   Deliberately plain: a title, a bar and a number. It is on screen for a few
   seconds at most and its whole job is to say "this is work, not a hang". */
#worldload { z-index: 2000; display: flex; align-items: center; justify-content: center;
  background: #050407f2; }
#worldload.hidden { display: none; }
#worldload .wlbox { width: min(420px, calc(84vw / var(--uiz, 1))); text-align: center; }
#worldload h2 {
  margin: 0 0 14px; font-size: 20px; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; color: #cfe8d6;
}
#worldload .wltrack {
  height: 12px; border: 1px solid #3f6a48; border-radius: 3px;
  background: #0d140f; overflow: hidden;
}
#worldload .wltrack i {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(90deg, #3f6a48, #7fd08a);
  transition: width .18s linear;
}
#worldload .wlpct { margin-top: 8px; font-size: 13px; color: #9fd8aa; font-variant-numeric: tabular-nums; }
#worldload .wlsub { margin: 6px 0 0; font-size: 11px; color: #6f8f78; letter-spacing: .05em; }
#mendbar { position: absolute; left: 0; right: 0; bottom: calc(110px + var(--xrow, 51px)); display: flex; justify-content: center; pointer-events: none; }
#mendbar > * { pointer-events: auto; }
/* WIDE, AND IT SAYS WHAT IT IS. A hotbar slot is 76 wide because there are
   ten of them in a row; there is one of these, it is not competing for space,
   and the whole reason it exists is that you should never have to hunt for
   it. So it is wide enough to carry the word and the key side by side at a
   readable size instead of a number tucked in a corner. */
/* ============ THE NAME OVER THE KEY, NOT BESIDE IT ==================
   These sat in a ROW - name, gap, key - which was fine while there was one
   of them and ragged the moment there were three. "Mend" is four characters
   and "Rejuvenation" is twelve, so with the pair centred as a unit every
   name started at a different place and every key landed at a different
   place, and the three cells read as three unrelated things.

   Stacked, both centred, the names line up down the middle of the shelf and
   so do the keys. The name is the thing you look for, so it stays the same
   size and keeps the top line to itself; the key drops underneath in small
   type, because you need it once and then your hand remembers. */
#mendbar .hslot {
  width: 156px; flex: 0 0 156px; height: 46px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  border-color: #3f6a48; background: #131b15b0;
}
#mendbar .hslot .ab {
  font-size: 14px; font-weight: 600; letter-spacing: .5px;
  line-height: 1.1; text-align: center;
}
/* the key drawn as a key, so it reads as "press this" rather than as part of
   the name - the same trick the tooltips use. Small, quiet, and centred under
   the word it belongs to. */
#mendbar .hslot .mkey {
  font-size: 10px; letter-spacing: .04em;
  color: #8cc79a; background: #0d140f;
  border: 1px solid #35583c; border-radius: 3px; padding: 0 5px;
  line-height: 14px; text-align: center;
}
/* not yours yet: dimmed, desaturated, and it does not light up when the
   pointer crosses it - the cell should feel inert rather than broken */
#mendbar .hslot.locked { opacity: .42; filter: grayscale(0.7); }
#mendbar .hslot.locked .ab { color: #8a9a8e; }
#mendbar .hslot.locked:hover { border-color: #3f6a48; }
#mendbar .hslot:hover { border-color: #7fd08a; }
#mendbar .hslot.cooling { opacity: .55; }
/* a mend that has just opened - see refreshMendCell (owner, 2026-09-28: "Mend does not look visibly unlocked once you get it") */
#mendbar .hslot.fresh { border-color: #ffd86a; animation: mendfresh 1.1s ease-in-out infinite; }
#mendbar .hslot.fresh .ab { text-shadow: 0 0 6px rgba(255, 216, 106, .8); }
@keyframes mendfresh { 0%, 100% { box-shadow: 0 0 0 1px #ffd86a, 0 0 6px 1px rgba(255, 216, 106, .35); } 50% { box-shadow: 0 0 0 2px #ffe9a8, 0 0 16px 5px rgba(255, 216, 106, .7); } }
@media (prefers-reduced-motion: reduce) { #mendbar .hslot.fresh { animation: none; box-shadow: 0 0 0 2px #ffd86a, 0 0 12px 3px rgba(255, 216, 106, .6); } }
/* it sits directly on top of the weapon row, which ends at 106 - and the two
   progress bars that used to live in this band have been moved up past it.
   Their old homes (116 and 140) are inside 110..150 and they overlapped. */
#weaponbar { position: absolute; left: 0; right: 0; bottom: calc(66px + var(--xrow, 51px)); display: flex; justify-content: center; gap: 4px; pointer-events: none; }
#weaponbar > * { pointer-events: auto; }
.wslot {
  position: relative; width: 40px; height: 40px; background: #16151db0;
  border: 1px solid #3a3748; border-radius: 5px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.wslot:hover { border-color: #6a6480; }
.wslot.on { border-color: #e8a04a; box-shadow: 0 0 8px #e8702c55; background: #241d16d0; }
.wslot.missing { opacity: .38; }
.wslot .num { position: absolute; left: 2px; top: 0; font-size: 9px; color: #6a6780; }
/* the health potions' shared wait (owner, 2026-09-30 - ONE POTION, THEN A WAIT), swept over each potion on the F-keys and in the bag */
.wslot .pcd, .slot .pcd { position: absolute; left: 0; bottom: 0; width: 100%; height: 0; background: #000000a0; pointer-events: none; border-radius: 0 0 4px 4px; }
/* how many potions are left on a slot that holds something usable */
.wslot .wq { position: absolute; right: 2px; bottom: 0; font-size: 10px; font-weight: 600;
  color: #e8d8a0; text-shadow: 0 1px 2px #000; }
.wslot img { width: 30px; height: 30px; image-rendering: pixelated; }
.slot.sel { border-color: #e8a04a; box-shadow: 0 0 0 1px #e8a04a inset; }

/* the recall button and the ten ability keys are one row, so the portal is
   always the thing immediately left of the "1" */
/* The two vials sit IN the bar row now, one at each end, so they are always
   beside it whatever the row's width works out to, and no longer floating
   over the middle of it. They never shrink: a narrow window is allowed to
   squeeze the ability slots and is not allowed to squash the thing telling
   you whether you can cast.

   The selector carries #barrow on purpose - `#barrow > *` turns pointer
   events back on for everything in the row, and a bare `.globe` loses to it,
   which would leave a sixty pixel square either side of the bar quietly
   eating clicks meant for the ground. */
#barrow > .globe { image-rendering: pixelated; pointer-events: none;
  flex: 0 0 auto; align-self: center; margin-bottom: -6px; }
#barrow { position: absolute; left: 0; right: 0; bottom: 14px;
  display: flex; justify-content: center; align-items: flex-end; gap: 5px;
  pointer-events: none; }
#barrow > * { pointer-events: auto; }
#hotbar { display: flex; gap: 5px; min-width: 0; }

/* ---- what the wisp is saying ----
   Small, yellow, and unmistakably not the game talking to you: no panel, no
   border, no background plate. It is lit from behind by its own shadow so it
   stays readable over grass, stone and water alike without a box round it.
   Positioned by transform only - never by left/top - so following the wisp
   never touches layout. */
#wisptalk {
  position: absolute; left: 0; top: 0; z-index: 6;
  max-width: 260px; padding: 0;
  font-size: 12px; line-height: 1.32; font-style: italic;
  color: #ffd970; text-align: center;
  text-shadow: 0 0 4px #0b0a10, 0 1px 0 #0b0a10, 1px 0 0 #0b0a10,
               -1px 0 0 #0b0a10, 0 -1px 0 #0b0a10;
  pointer-events: none; opacity: 0;
  transition: opacity .45s ease-in-out;
  /* the element's own anchor is its bottom centre, so the transform can be a
     plain "put it here" with no arithmetic about the text's width */
  transform: translate(-50%, -100%);
  will-change: transform;
}
#wisptalk.on { opacity: 1; }
#wisptalk.hidden { display: none; }

/* ============ THE CRIER'S LINE =========================================
   The wisp's line, in the crier's colour. Same anchoring - bottom centre,
   moved by transform only - and a wider box, because he says whole sentences
   of news rather than one dry remark. Warmer and less golden than the wisp
   so that two voices on screen at once are still two voices. */
#criertalk {
  position: absolute; left: 0; top: 0; z-index: 6;
  max-width: 320px; padding: 0;
  font-size: 12.5px; line-height: 1.34; font-weight: 600;
  color: #ffca8a; text-align: center;
  text-shadow: 0 0 4px #0b0a10, 0 1px 0 #0b0a10, 1px 0 0 #0b0a10,
               -1px 0 0 #0b0a10, 0 -1px 0 #0b0a10;
  pointer-events: none; opacity: 0;
  transition: opacity .45s ease-in-out;
  transform: translate(-50%, -100%);
  will-change: transform;
}
#criertalk.on { opacity: 1; }
#criertalk.hidden { display: none; }


/* ---- the shared beat, in the middle of the screen ----
   Twenty-four blocks round a circle, emptying anticlockwise. Full ring =
   wait, no ring = strike.

   It is pinned to the CENTRE OF THE SCREEN and never moves: a cooldown is a
   fact about you, and a fact about you does not slide around the world. The
   camera keeps you at that same point, so it reads as a ring about your feet
   without being drawn there - which is the whole difference, because the old
   one was re-stroked on the world canvas every frame and inherited every
   hitch the world had.

   Doubled on purpose: the canvas is 52 art pixels shown at 104, so every
   block is a whole two-by-two of screen pixels and matches the vials. */
#gcdring {
  position: absolute; left: 50%; top: 50%;
  width: 104px; height: 104px; margin: -52px 0 0 -52px;
  image-rendering: pixelated; pointer-events: none; z-index: 4;
  opacity: 0; transition: opacity .12s ease-out;
}
#gcdring.on { opacity: .92; }
.hslot {
  position: relative; width: 76px; height: 46px; flex: 0 1 76px; background: #16151db0;
  border: 1px solid #3a3748; border-radius: 5px; cursor: pointer; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.hslot.sel { border-color: #e8a04a; box-shadow: 0 0 10px #e8702c55; }
.hslot .num { position: absolute; left: 3px; top: 1px; font-size: 10px; color: #6a6780; }
.hslot .ab { font-size: 11px; text-align: center; padding: 0 4px; }
.hslot .cd { position: absolute; left: 0; bottom: 0; width: 100%; background: #000000a0; height: 0; }

/* 30: this used to sit at 74px, right on top of the weapon bar. The hotbar
   ends at 60 and the weapon bar at 106, so clear both. */
#gatherbar { position: absolute; left: 50%; bottom: calc(160px + var(--xrow, 51px)); transform: translateX(-50%); width: 220px; height: 16px; background: #0c0b10; border: 1px solid #2b2937; border-radius: 3px; }
#gatherbar i { display: block; height: 100%; background: #7fd08a; }
#gatherbar span { position: absolute; inset: 0; text-align: center; font-size: 10px; line-height: 16px; }
/* Stop, beside the bar while a stream of crafts runs (2026-10-10) */
#gatherbar .gbstop { position: absolute; left: calc(100% + 6px); top: -2px; height: 20px; padding: 0 9px; font-size: 11px;
  cursor: pointer; pointer-events: auto; background: #3a1e1e; color: #f0d8d0; border: 1px solid #8a4a40; border-radius: 3px; }
#gatherbar .gbstop:hover { background: #5a2a26; }
/* the recall channel - the same bar, in the blue of the way home */
#recallbar { position: absolute; left: 50%; bottom: calc(184px + var(--xrow, 51px)); transform: translateX(-50%); width: 260px; height: 18px;
  background: #0c0b10; border: 1px solid #35405a; border-radius: 3px; box-shadow: 0 0 12px #6aa9e04a; }
#recallbar i { display: block; height: 100%; background: linear-gradient(90deg, #4a7ab8, #a9d8ff); }
#recallbar span { position: absolute; inset: 0; text-align: center; font-size: 10px; line-height: 18px;
  color: #e8f2ff; text-shadow: 0 1px 2px #000; letter-spacing: 1px; }
/* THE WAY HOME, LEFT OF THE ACTION BAR. A blue portal: a ring of light with a
   darker eye in the middle, turning slowly, and turning hard while it winds
   up. It sits in the gap the hotbar leaves on the left. */
/* The button rides IN the row now rather than at a fixed offset. The hotbar
   centres itself, so a button pinned at left:296px only happened to sit
   beside the "1" key at one window width and drifted away from it at every
   other. Being a sibling in the same flex row, it is always touching it. */
#btn-recall {
  position: relative; width: 52px; height: 46px; flex: 0 0 52px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  padding: 0; background: #101722d0; border: 1px solid #35405a; border-radius: 5px;
  cursor: pointer; color: #8fb8e0;
}
#btn-recall:hover { border-color: #6aa9e0; color: #cfe6ff; }
/* A SPRITE STRIP, NOT A GRADIENT. Eight 15x15 frames drawn in ui.js, stepped
   through so the ring turns a whole frame at a time - no rotation, so no pixel
   is ever resampled off the grid. `pixelated` keeps the blocks hard when the
   26px box blows the 15px art up. */
#btn-recall .portal {
  display: block; width: 26px; height: 26px;
  background-repeat: no-repeat;
  background-size: 800% 100%;
  image-rendering: pixelated;
  filter: drop-shadow(0 0 5px #4a7ab8cc);
  animation: portalframes 6s steps(8, jump-none) infinite;
}
#btn-recall .rlabel { font-size: 8px; letter-spacing: 1px; text-transform: uppercase; line-height: 1; }
/* A PERCENTAGE background-position IS NOT A PERCENTAGE OF THE IMAGE. It is a
   percentage of (box width - image width), so with an image eight boxes wide
   0% is the first frame and 100% is the eighth - and the -800% this first had
   flung the sprite miles off the box, which is why the portal showed one
   frame and then vanished for the rest of the loop.
   `jump-none` is the other half: plain steps(8) would land on 0/8..7/8 and
   never reach the last frame, while jump-none gives the eight positions
   0/7..7/7 - exactly one per frame. */
@keyframes portalframes { from { background-position-x: 0%; } to { background-position-x: 100%; } }
#btn-recall.channelling { border-color: #a9d8ff; background: #16263ce0; color: #dff0ff; }
#btn-recall.channelling .portal { animation-duration: 0.9s; filter: drop-shadow(0 0 9px #a9d8ff); }
#btn-recall.cooling { opacity: .5; }
#btn-recall.cooling .portal { animation-duration: 24s; filter: grayscale(.7); }

/* GLORY, ROLLING UP OVER THE CHAT. Small yellow lines that climb and fade -
   what somebody just got, and what multiplied it. */
#glorytick {
  position: absolute; left: 12px; bottom: calc(208px + var(--xrow, 51px)); width: 620px; max-width: calc(calc(100vw / var(--uiz, 1)) - 24px);
  display: flex; flex-direction: column-reverse; gap: 2px; pointer-events: none; overflow: hidden;
}
#glorytick .gline {
  font-size: 11px; color: #ffe08a; text-shadow: 0 1px 3px #000, 0 0 8px #00000090;
  opacity: 0; transform: translateY(8px);
  animation: gloryroll 6s ease-out forwards;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#glorytick .gline.big { color: #ffd24a; font-weight: bold; }
#glorytick .gline b { color: #fff3c4; }
#glorytick .gline .mult { color: #ffb44a; }
@keyframes gloryroll {
  0%   { opacity: 0; transform: translateY(10px); }
  8%   { opacity: 1; transform: translateY(0); }
  70%  { opacity: 1; transform: translateY(-6px); }
  100% { opacity: 0; transform: translateY(-16px); }
}

#worldtip { position: absolute; left: 50%; top: 14px; transform: translateX(-50%); background: #12111ad0; border: 1px solid #2b2937; padding: 4px 10px; border-radius: 4px; font-size: 12px; color: #d8d5e4; }

/* Peder's ledger, at the bottom of his own window. Deliberately quiet - it
   is a fee, not a shop, and he would not advertise it. */
.fee { margin-top: 9px; padding: 8px 10px; border-radius: 5px;
       background: #14121cc0; border: 1px solid #2b2937;
       border-left: 2px solid #b5852f;
       display: flex; flex-direction: column; gap: 4px; }
.fee > b { color: #ffe08a; font-size: 12px; }
.fee .muted { font-size: 11px; color: #9a97a8; font-style: italic; }
.fee .row { gap: 5px; margin: 2px 0 0; }
.fee button { font-size: 11px; padding: 4px 9px; }
.fee button:disabled { opacity: .45; cursor: not-allowed; }

/* --------------------------------------------- a skill has just opened ----
   Middle of the screen, because it is worth stopping for - and an arrow
   underneath it bouncing at the hotbar, because the news is useless unless
   you know where to look. It never blocks a click: the pointer goes through
   it to the world, so it cannot eat a cast in the middle of a fight. */
#unlockpop {
  position: absolute; left: 50%; top: 42%; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 14px 26px 10px; border-radius: 7px; text-align: center;
  background: #171326e8; border: 1px solid #b5852f;
  box-shadow: 0 0 22px #e8a04a44, 0 6px 34px #000a, inset 0 0 0 1px #e8a04a22;
  pointer-events: none; z-index: 60;
  animation: unlockin .34s cubic-bezier(.2, 1.5, .4, 1);
}
#unlockpop.out { animation: unlockout .45s forwards; }
/* ---- a skill point: the quick gold card (owner, 2026-09-29) - see pointCard ---- */
#pointcard {
  position: absolute; left: 50%; top: 30%; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 12px 24px 11px; border-radius: 7px; text-align: center; cursor: pointer;
  background: #1b1526ee; border: 1px solid #d9a441;
  box-shadow: 0 0 26px #f0b95466, 0 6px 34px #000b, inset 0 0 0 1px #f0c86a2a;
  z-index: 61; animation: unlockin .34s cubic-bezier(.2, 1.5, .4, 1);
}
#pointcard.hidden { display: none; }
#pointcard.out { animation: unlockout .42s forwards; pointer-events: none; }
#pointcard b { font-size: 15px; letter-spacing: .14em; text-transform: uppercase; color: #ffd978; text-shadow: 0 0 8px #f0b95488; }
#pointcard span { font-size: 12px; color: #d8cfe8; }
#pointcard:hover { border-color: #ffe29a; }
#pointcard .pcstar { width: 18px; height: 18px; background: #ffd978; clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
  filter: drop-shadow(0 0 6px #ffcf6a); animation: pcspin 3.2s linear infinite; }
@keyframes pcspin { from { transform: rotate(0deg) scale(1); } 50% { transform: rotate(45deg) scale(1.15); } to { transform: rotate(90deg) scale(1); } }
@media (prefers-reduced-motion: reduce) { #pointcard, #pointcard .pcstar { animation: none; } }

/* ---- a new square of the world ------------------------------------------
   Deliberately NOT the unlock popup's gold. A skill unlock is the game
   telling you that you have grown; this is the game telling you that you
   have been somewhere, and the atlas, the board and the tiles are all the
   cold blue-green of old ink and vellum. Same geometry, same timing, its
   own colour, so the two read as different kinds of news at a glance. */
#foundpop {
  position: absolute; left: 50%; top: 38%; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 14px 30px 12px; border-radius: 7px; text-align: center;
  background: #101a26e8; border: 1px solid #4e8fa8;
  box-shadow: 0 0 22px #6fd8ea44, 0 6px 34px #000a, inset 0 0 0 1px #6fd8ea22;
  pointer-events: none; z-index: 60;
  animation: unlockin .34s cubic-bezier(.2, 1.5, .4, 1);
}
#foundpop.out { animation: unlockout .45s forwards; }
#foundpop b {
  font-size: 15px; color: #bfeaff; letter-spacing: .04em;
  text-shadow: 0 0 12px #6fd8ea88;
}
/* the tile itself, at eight times, crisp - it is sixteen pixels of real map
   and smoothing it would turn the ground into soup */
#foundtile {
  width: 128px; height: 128px; image-rendering: pixelated;
  border-radius: 3px; box-shadow: 0 0 18px #6fd8ea33, 0 2px 10px #000a;
}
#foundname { font-size: 14px; color: #eaf6ff; letter-spacing: .02em; }
#foundcount { font-size: 12px; color: #8fb6cc; font-variant-numeric: tabular-nums; }
#unlockpop b {
  font-size: 15px; color: #ffe08a; letter-spacing: .04em;
  text-shadow: 0 0 12px #e8a04a88;
}
#unlocknames {
  font-size: 13px; color: #fff3d0; max-width: 340px; line-height: 1.45;
}
/* THE ARROW STANDS OVER AN EMPTY SLOT, not under the words. A chevron in
   the middle of the screen only says "look down"; parked above the gap on
   the bar it says exactly where the new ability has to go. Fixed to the
   viewport because it is placed from the slot's own screen position. */
#unlockarrow {
  position: fixed; z-index: 61; pointer-events: none;
  width: 15px; height: 15px; margin-left: -7px;
  border-right: 3px solid #ffd06a; border-bottom: 3px solid #ffd06a;
  transform: rotate(45deg);
  animation: unlockbob 1s ease-in-out infinite;
  filter: drop-shadow(0 0 6px #e8a04abb);
}
/* and the slot it is pointing at, lit so the pair read as one thing */
.hslot.unlockme {
  border-color: #b5852f;
  box-shadow: 0 0 12px #e8a04a55, inset 0 0 0 1px #e8a04a33;
}
@keyframes unlockin {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(.86); }
  100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes unlockout {
  0%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -58%) scale(.97); }
}
@keyframes unlockbob {
  0%, 100% { transform: rotate(45deg) translate(0, 0); }
  50%      { transform: rotate(45deg) translate(4px, 4px); }
}
/* ============ THE SAME ARROW, POINTING UP =============================
   Owner, 2026-10-01: "The arrow that's supposed to point to the air gauge
   is upside down. it's currently showing "v" instead of "^"". pointAt()
   put it under the gauge and turned it with an inline rotate(180deg) - but
   the bob above sets `transform` on every frame, and an animation beats an
   inline style, so it went on pointing down. Pointing up is its own class
   with its own bob: the chevron turned half way round (225 deg), nudging
   towards what it points at, and still up with the animation off. */
#unlockarrow.up { transform: rotate(225deg); animation-name: unlockbobup; }
@keyframes unlockbobup {
  0%, 100% { transform: rotate(225deg) translate(0, 0); }
  50%      { transform: rotate(225deg) translate(4px, 4px); }
}
@media (prefers-reduced-motion: reduce) {
  #unlockpop, #unlockpop .downarrow { animation: none; }
}

/* above the bars, not behind them - they are centred now and would otherwise
   be drawn straight over the left end of the chat */
#chatbox { position: absolute; left: 12px; bottom: calc(118px + var(--xrow, 51px)); width: 620px; max-width: calc(calc(100vw / var(--uiz, 1)) - 24px); }
/* two logs abreast: talk on the left, the world on the right */
.chatpanes { display: flex; gap: 4px; align-items: stretch; }
.chatpane { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.chatpane .ctab {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 9px; letter-spacing: 1px; text-transform: uppercase; color: #8b8898;
  background: #14121ce0; border: 1px solid #24222e; border-bottom: none;
  border-radius: 4px 4px 0 0; padding: 2px 6px;
}
.chatpane .ctab button {
  font-size: 9px; padding: 0 4px; line-height: 14px; background: none;
  border: 1px solid #2f2c3a; color: #7d7a8c; border-radius: 2px; cursor: pointer;
}
.chatpane .ctab button:hover { color: #e8a04a; border-color: #6a5a2a; }
#chatlog, #syslog { height: 150px; overflow: auto; background: #0f0e14a0; border: 1px solid #24222e; border-radius: 0; padding: 6px; font-size: 12px; }
#chatlog .line, #syslog .line { margin: 1px 0; color: #d8d5e4; word-wrap: break-word; }
#chatlog .line.sys, #syslog .line.sys { color: #8b8898; }
#chatlog b, #syslog b { color: #6ad8ff; }
/* A THING, LINKED IN CHAT (ui.js): its name in the loot builder's colours -
   plain, blue magic, gold rare (sealed too), orange a relic - and its whole
   tip on hover */
#chatlog .clink { color: #e9dfc4; cursor: help; border-bottom: 1px dotted currentColor; white-space: nowrap; }
#chatlog .clink:hover { background: #ffffff14; }
#chatlog .clink.ax1 { color: #8fbcff; }
#chatlog .clink.ax2, #chatlog .clink.axu { color: #f2c94c; }
#chatlog .clink.axq { color: #e8a04a; }
/* the world's own feed, colour-coded by what happened */
#syslog .line { color: #a9a5b8; }
#syslog .line.kill { color: #d8a06a; }
#syslog .line.loot { color: #c9b46a; }
#syslog .line.lvl  { color: #7fd08a; }
#syslog .line.warn { color: #e0806a; }
#syslog .line.gather { color: #b7cf9a; }   /* everything you gather - client/js/gatherlog.js */
#syslog .line .rep { color: #6d6a7a; font-size: 10px; }
.chatrow { display: flex; align-items: stretch; }
#modebadge {
  display: flex; align-items: center; padding: 0 8px; font-size: 10px; letter-spacing: 1px;
  background: #1b2a1b; border: 1px solid #2f4a2f; border-right: none; border-radius: 0 0 0 4px; color: #7fd08a;
}
#modebadge.chat { background: #2a2418; border-color: #6a5a2a; color: #ffe08a; }
#chatinput { border-radius: 0 0 4px 0; border-top: none; }
#chatbox.chatmode #chatinput { border-color: #e8a04a; background: #1d1a14; }
#chatbox.chatmode #chatlog { border-color: #4a3a20; }
#chatbox.chatmode .chatpane .ctab { border-color: #4a3a20; }
/* THE CHAT MOVES, AND GROWS (ui.js): its tab strips are its title bar, and a
   grip in the bottom-right corner sizes it */
#chatbox .chatpane .ctab { cursor: move; user-select: none; }
#chatbox .chatpane .ctab button { cursor: pointer; }
#chatbox .chatgrip {
  position: absolute; right: 0; bottom: 0; width: 14px; height: 14px; cursor: nwse-resize; z-index: 2;
  background:
    linear-gradient(135deg, transparent 0 45%, #8b8898 45% 52%, transparent 52% 64%, #8b8898 64% 71%, transparent 71% 83%, #8b8898 83% 90%, transparent 90%);
  opacity: .55;
}
#chatbox .chatgrip:hover, body.chatsizing #chatbox .chatgrip { opacity: 1; }
#chatbox .chatrow { padding-right: 14px; }
body.chatsizing, body.chatsizing * { cursor: nwse-resize !important; user-select: none !important; }

#worldmapcanvas { display: block; background: #08070b; border: 1px solid #2b2937; cursor: grab; }
#worldmapcanvas:active { cursor: grabbing; }
.panel h3 { user-select: none; }

#toasts { position: absolute; left: 50%; top: 60px; transform: translateX(-50%); display: flex; flex-direction: column; gap: 4px; align-items: center; }
.toast { background: #16151dd8; border: 1px solid #3a3748; padding: 6px 12px; border-radius: 4px; font-size: 12px; transition: opacity .5s; }
.toast.good { border-color: #5ec86a; color: #a8e8b0; }
/* THE THIRD TOAST. There were two - neutral and green - so anything that
   needed to be noticed had to borrow the success colour, and a refusal
   painted in the colour of success is worse than no message at all. */
.toast.bad  { border-color: #d0455a; color: #ffb0a8; background: #2a1418e8; }
.toast.out { opacity: 0; }

/* -------------------------------------------------------------- panels */
.panel {
  /* max-height is a last resort only - the slot windows size themselves to
     the screen so they never reach it */
  position: absolute; width: 320px; max-height: calc(92vh / var(--uiz, 1)); overflow: auto;
  background: #14131bee; border: 1px solid #34313f; border-radius: 7px; padding: 12px;
  box-shadow: 0 12px 40px #000a;
}
.panel.wide { width: 420px; }
.panel h3 { margin: 0 0 10px; font-size: 14px; color: #e8a04a; display: flex; justify-content: space-between; align-items: center; }
.panel h3 .close { padding: 1px 7px; font-size: 12px; }
.panel .row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.panel .row input { flex: 1; }
/* 23: the search box rides along at the top of a long list instead of
   scrolling away the moment you start looking */
.panel .row.sticky {
  position: sticky; top: -12px; z-index: 3;
  background: #14131bfa; margin: 0 -12px 8px; padding: 8px 12px;
  border-bottom: 1px solid #2b2937;
}
/* 26: a little x inside every search box */
.searchwrap { position: relative; flex: 1; display: flex; }
.searchwrap input { flex: 1; padding-right: 22px; }
.searchwrap .clr {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; line-height: 14px; text-align: center;
  border: 0; background: #2b2937; color: #b9b4c6; border-radius: 8px;
  font-size: 11px; cursor: pointer; padding: 0;
}
.searchwrap .clr:hover { background: #46425a; color: #fff; }
.searchwrap .clr.hidden { display: none; }
/* 24: our own item tooltip - quicker to appear, slower to leave */
.itemtip {
  position: fixed; z-index: 1001; max-width: 280px; pointer-events: none;
  background: #16151df2; border: 1px solid #46425a; border-radius: 5px;
  padding: 6px 9px; font-size: 12px; line-height: 1.45; color: #ded9e8;
  white-space: pre-line; box-shadow: 0 8px 24px #000b;
}
.itemtip.hidden { display: none; }
/* ---- is it better than the one I have on? ----
   The tip's own text is pre-line, which would put a newline between every
   cell of a grid - so the block turns that off and lays out five columns:
   what, yours, an arrow, this one, the change. Green when the new piece wins
   that line and red when it loses, on the new figure AND on the change, so
   the answer is legible without reading a word of it. */
.itemtip .cmp {
  white-space: normal; margin-top: 7px; padding-top: 6px;
  border-top: 1px solid #3a3548;
}
.itemtip .cmph {
  font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  color: #9a93ad; margin-bottom: 5px;
}
.itemtip .cmph b { display: block; color: #e8d9a8; letter-spacing: 0;
  text-transform: none; font-size: 12px; font-weight: 600; }
.itemtip .cmpg {
  display: grid; grid-template-columns: auto auto auto auto auto;
  gap: 2px 7px; align-items: baseline;
  font-variant-numeric: tabular-nums;
}
.itemtip .cn { color: #a9a2ba; }
.itemtip .cv { text-align: right; color: #cfc9dc; }
.itemtip .cx { color: #6a6480; }
.itemtip .cd { text-align: right; font-weight: 600; }
/* the two colours the whole thing exists for */
.itemtip .up   { color: #7fd08a; }
.itemtip .down { color: #ee6a5e; }
.itemtip .same { color: #cfc9dc; }
/* the tally sits in the change column like any other row - its first three
   cells are empty, so it lines up under the figures it is counting */
.itemtip .cmpg .cd:last-child { padding-top: 3px; }
/* 7: the little menu you get from right-clicking something in your bag */
.itemmenu {
  position: fixed; z-index: 1000; min-width: 150px; padding: 5px;
  background: #16151dfa; border: 1px solid #46425a; border-radius: 6px;
  box-shadow: 0 10px 30px #000c; display: flex; flex-direction: column; gap: 2px;
}
.itemmenu .imhead { font-size: 11px; color: #e8a04a; padding: 3px 6px 5px; border-bottom: 1px solid #2b2937; margin-bottom: 3px; }
.itemmenu button { text-align: left; background: none; border: 0; padding: 5px 8px; border-radius: 4px; font-size: 12px; color: #d8d4e0; }
.itemmenu button:hover { background: #2f2b3d; }
.itemmenu button.dim { color: #6a6780; }
.itemmenu .imnote { font-size: 10px; color: #d08a6a; padding: 3px 8px 1px; }
.slot.locked { box-shadow: 0 0 0 1px #c9b46a inset; }
.slot .lockpip { position: absolute; left: 1px; top: 0; font-size: 8px; opacity: .85; }
/* ============ AND THE ONE WORTH STOPPING FOR ========================
   A green arrow in the top-right of anything in your bag that beats what you
   are wearing in that slot - see upArrow in ui.js for what earns one. Top
   right because the quantity already owns the bottom right and the padlock
   the top left, so this is the last free corner and never collides with
   either. The glow is what makes it findable at a glance in a full bag
   without it being loud enough to notice when you are not looking for it. */
/* ---- a piece of gear wears its own level ---------------------------------
   A small number bottom-left and a border on the square, both in the colour
   of where that level stands: grey, green, blue, purple, red, orange. Same
   six bands as the badge under the doll, so a square full of orange squares
   is exactly the kit that scores sixty.

   Bottom-left because it is the only free corner - padlock top-left, upgrade
   arrow top-right, stones down the right edge, stack count bottom-right. */
.slot.graded { border-color: color-mix(in srgb, var(--il) 62%, #2b2937); }
.slot.graded:hover { border-color: var(--il); }
.slot .ilvl {
  position: absolute; left: 2px; bottom: 0; z-index: 2;
  font-size: 9px; line-height: 10px; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--il); text-shadow: 0 1px 2px #000, 0 0 3px #000;
  pointer-events: none;
}
/* the best there is for that slot, and it should not need reading twice */
.slot .ilvl.best { text-shadow: 0 1px 2px #000, 0 0 6px var(--il); }
.slot.graded .ilvl.best ~ img { filter: drop-shadow(0 0 3px color-mix(in srgb, var(--il) 45%, transparent)); }
.slot .uppip {
  position: absolute; right: 1px; top: 0; z-index: 2;
  font-size: 9px; line-height: 9px; color: #5ee08a;
  text-shadow: 0 0 3px #1e6b3a, 0 1px 1px #000;
  pointer-events: none;
}
/* the stones run down this same edge, so they start below the arrow rather
   than under it - only on the squares that actually have one */
.slot.up .gempips { top: 10px; }
/* THE STONES IN A PIECE, down the right-hand edge of its square. Filled dots
   are set, hollow ones are holes still waiting - so a full set and an empty
   one are told apart at a glance without reading anything. They sit ON the
   picture rather than under it, because the grid squares are a fixed size and
   anything that changes their height reflows the whole bag.

   Down the side rather than along the bottom: the stack count lives in the
   bottom right corner, and centring the column vertically keeps the two apart
   without either having to move. */
.gempips { position: absolute; right: 1px; top: 0; bottom: 0; left: auto;
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  pointer-events: none; }
.gempip { width: 4px; height: 4px; border-radius: 50%; background: #cfcfe0;
  box-shadow: 0 0 3px #000a, 0 0 2px currentColor; }
.gempip.empty { background: none; box-shadow: none; border: 1px solid #6a6478; }
/* THE TENSION GAUGE. The sweet spot is drawn as a wide green field rather
   than a line, because it IS wide - the game is keeping a rhythm, not
   threading a needle, and the picture has to say so before the player has
   worked it out the hard way. Red when you are outside it. */
.fishbox { min-width: 340px; }
.fishten { position: relative; height: 18px; margin: 12px 0 4px; border-radius: 4px;
  background: #14121a; border: 1px solid #3a3748; overflow: hidden; }
.fishsweet { position: absolute; top: 0; bottom: 0; background: #2e6a3c55;
  border-left: 1px solid #4e9a5c; border-right: 1px solid #4e9a5c; }
.fishfill { position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  background: linear-gradient(90deg, #4e9a5c88, #7fd08a); transition: width .12s linear; }
.fishten.out .fishfill { background: linear-gradient(90deg, #a0404088, #e0606a); }
.fishlbl { position: absolute; left: 8px; top: 2px; font-size: 9px; letter-spacing: .14em;
  color: #cfd8e8; text-shadow: 0 1px 2px #000; pointer-events: none; }
.fishbox.snapping .fishten { box-shadow: 0 0 10px #e0606a; }
/* the one-off fishing lesson - readable prose, not a wall of hint text */
.lesson p { margin: 8px 0; font-size: 12.5px; line-height: 1.5; color: #cfc8bb; }
.lesson b { color: #e8c14a; letter-spacing: .04em; }
/* ============ A BOOK, AND EVERYTHING WRITTEN ON PAPER ==================
   Deliberately NOT the game's panel look. Every other window in the vale is a
   dark slab with amber edges because it is a piece of equipment - a bag, a
   bar, a tree of skills. A thing somebody WROTE is not equipment: it is paper,
   it is the wrong colour for this world on purpose, and it should feel like
   holding an object rather than opening a menu.

   Built as one reader for all of them (see openBook) because he is adding
   more of these - a chart today, a journal or a ledger next - and the second
   one should cost nothing but its words. */
#bookwrap {
  position: fixed; inset: 0; z-index: 940;
  display: flex; align-items: center; justify-content: center;
  background: #05040788;
}
#bookwrap.hidden { display: none; }
.book {
  width: min(640px, calc(94vw / var(--uiz, 1))); max-height: calc(82vh / var(--uiz, 1));
  display: flex; flex-direction: column;
  background: #e4d7b8;
  color: #3a2f1e;
  border: 1px solid #6b5836;
  border-radius: 3px;
  box-shadow: 0 18px 50px #000000b0, inset 0 0 60px #c9b892;
  font-family: Georgia, 'Times New Roman', serif;
}
/* the darker edge where a page is bound, and the grain over the whole leaf */
.book::before {
  content: ''; position: absolute; pointer-events: none;
}
.bookhead {
  padding: 11px 20px 6px;
  border-bottom: 1px solid #b9a67e;
  display: flex; align-items: baseline; gap: 10px;
}
.bookhead h2 {
  margin: 0; font-size: 19px; font-weight: 600; letter-spacing: .3px;
  color: #4a3a22; font-family: Georgia, 'Times New Roman', serif;
}
.bookhead .by { font-size: 12px; font-style: italic; color: #7c6a49; }
.bookbody {
  padding: 10px 20px 14px; overflow-y: auto; line-height: 1.42; font-size: 13px;
}
.bookbody p { margin: 0 0 7px; }
.bookbody .aside { font-style: italic; color: #7c6a49; }
/* an entry in a chart or a ledger: a name, a line about it, and the dry facts */
.bookrow { padding: 4px 0 5px; border-bottom: 1px dotted #b9a67e; }
.bookrow:last-child { border-bottom: 0; }
.bookrow b { font-size: 13.5px; }
/* ============ THE NAME, OUTLINED ======================================
   Owner, 2026-09-26: the fish names on Hampus's chart are "hard to read
   against the surface" - each is written in the fish's own colour, and the
   pale ones all but vanish into the parchment. A 1px black outline (all
   eight neighbours, so it holds on every side) keeps the colour, which is
   the point of it, and makes every one of them legible. */
.bookrow .brhead > b {
  text-shadow: -1px 0 #000, 1px 0 #000, 0 -1px #000, 0 1px #000,
    -1px -1px #000, 1px -1px #000, -1px 1px #000, 1px 1px #000;
  letter-spacing: .2px;
}
.bookrow .brhead > b.inkdark { text-shadow: 1px 1px 0 #000; }
/* the name on the left, the tally out at the right margin, one line */
.bookrow .brhead {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
/* what you have actually landed. Green, because it is the one thing on the
   page that is yours rather than Hampus's. */
.bookrow .caught {
  font-size: 12px; color: #2f7a3a; white-space: nowrap;
  font-family: ui-monospace, Consolas, monospace;
}
.bookrow .caught b { font-size: 12px; color: #2f7a3a; }
.bookrow .said { display: block; font-size: 12px; margin: 1px 0 0; font-style: italic; }
.bookrow .facts {
  display: block; font-size: 11px; letter-spacing: 0;
  color: #7c6a49; font-family: ui-monospace, Consolas, monospace;
  white-space: nowrap; overflow-x: auto;
}
.bookrow.faint { opacity: .55; }
.bookfoot {
  padding: 10px 22px 16px; display: flex; justify-content: flex-end; gap: 10px;
  border-top: 1px solid #b9a67e;
}
.bookfoot button {
  font-family: Georgia, serif; font-size: 13px; cursor: pointer;
  background: #d3c4a0; color: #3a2f1e;
  border: 1px solid #8a7550; border-radius: 3px; padding: 5px 14px;
}
.bookfoot button:hover { background: #dfd2b1; border-color: #6b5836; }
.bookfoot button:focus-visible { outline: 2px solid #6b5836; outline-offset: 2px; }

/* ============ THE TICK BOX, DRAWN LIKE THE REST OF THE GAME ===========
   Two things were wrong with the first cut. It used the browser's own
   checkbox, which on Windows is a bright blue-and-white control that belongs
   to the operating system and not to a dim fantasy panel - and it was boxed
   and padded like a heading, so a footnote sat there as the loudest thing in
   the window.

   The native box is taken out of the layout (opacity, not display:none, so it
   is still focusable and still reachable by keyboard) and a small square is
   drawn in its place from the same greys and the same amber as every other
   panel. Quiet by default, amber only when it is actually ticked, and sized
   to the sentence it belongs to rather than to itself. */
.lessonhide {
  display: flex; align-items: center; gap: 7px;
  margin: 12px 0 2px;
  cursor: pointer; user-select: none;
  font-size: 11.5px; color: #8f8879;
}
.lessonhide:hover { color: #b9b1a2; }
.lessonhide input {
  position: absolute; opacity: 0; width: 0; height: 0; margin: 0;
}
.lessonhide .tick {
  flex: 0 0 auto; width: 12px; height: 12px;
  border: 1px solid #3a3748; border-radius: 2px;
  background: #16151d; position: relative;
}
.lessonhide:hover .tick { border-color: #5a5468; }
.lessonhide input:focus-visible + .tick { outline: 1px solid #e8a04a; outline-offset: 1px; }
/* the mark itself: two strokes, drawn rather than typed, so it does not
   depend on a glyph being present in whatever font ends up loading */
.lessonhide input:checked + .tick { border-color: #e8a04a; background: #241d16; }
.lessonhide input:checked + .tick::after {
  content: ''; position: absolute; left: 3px; top: 0px;
  width: 4px; height: 8px; border: solid #e8a04a;
  border-width: 0 1.5px 1.5px 0; transform: rotate(42deg);
}
.lessonhide input:checked ~ span { color: #c8c0b0; }
/* the remember box - a checkbox on a line, not a stacked label like the
   password field above it, or it reads as another thing to fill in */
.checkrow { display: flex !important; flex-direction: row !important; align-items: center;
  gap: 9px; font-size: 12px; color: #b8b3c4; cursor: pointer;
  margin: 10px 0 14px; padding: 9px 11px; line-height: 1.4;
  background: #1b1a24; border: 1px solid #302d3c; border-radius: 6px; }
.checkrow:hover { border-color: #45415a; color: #d8d3e0; }
.checkrow input { width: 15px; height: 15px; flex: 0 0 15px; margin: 0; accent-color: #e8a04a; }
.checkrow span { flex: 1; }
/* the recipes a villager will teach - the same furniture as a craft row, so
   it reads as the same kind of thing in a different place */
/* the counter's sections. Same switches as the crafting panel, because they
   are the same idea in a different window and there is no reason to invent a
   second look for it. */
.dialogtabs { display: flex; gap: 4px; margin: 10px 0 8px; flex-wrap: wrap; }
.dialogtab {
  padding: 4px 12px; font-size: 12px; border-radius: 4px; cursor: pointer;
  background: #1e1b28; border: 1px solid #3a3748; color: #cfc8bb;
}
.dialogtab:hover { background: #262234; border-color: #4e4a60; }
.dialogtab.on { background: #33291a; border-color: #8a7434; color: #ffe9b0; font-weight: 600; }
.reciperow { display: flex; gap: 8px; align-items: center; padding: 5px 0; border-top: 1px solid #262433; }
.reciperow:first-of-type { border-top: 0; }
.reciperow img { width: 26px; height: 26px; image-rendering: pixelated; flex: 0 0 26px; }
.reciperow .rbody { flex: 1; display: flex; flex-direction: column; font-size: 11px; min-width: 0; }
.reciperow .rbody b { color: #f0e4cc; font-size: 11.5px; }
.reciperow .rmats em { font-style: normal; }
.reciperow .rmats em.ok { color: #7fd08a; }
.reciperow .rmats em.no { color: #d08a6a; }
.reciperow .meta { color: #6d675d; font-size: 10px; }
.reciperow .rknown { color: #7fd08a; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; }
.reciperow.known { opacity: .62; }
/* the bin - dimmer than the vault, because nothing in it is yours any more
   in spirit, and framed in red so it never reads as more storage */
/* The deposit strip that used to live at the bottom of the vault is gone -
   your real bag opens beside it instead. It needed a rule here to stop it
   fighting the vault for height; nothing to fight now. */
.depgrid.bin { opacity: .92; }
.depgrid.bin .slot { border-color: #4a2c2c; background: #1a1416; }
.depgrid.bin .slot.empty { border-style: dashed; }
.depgrid.bin .slot.droptarget { border-color: #e0453f; box-shadow: 0 0 8px #e0453f66; }
button.danger { border-color: #7a3030; color: #ffb0a8; }
button.danger:hover:not(:disabled) { background: #3a1c1c; border-color: #e0453f; }
.splitrow { display: flex; gap: 8px; align-items: center; margin: 8px 0; }
/* the quiet line under the split box telling you the wheel works */
p.muted.dim { font-size: 10.5px; opacity: .7; margin-top: -2px; }
.splitrow input[type=range] { flex: 1; }
.splitrow input[type=number] { width: 80px; }
p.warn { color: #d08a6a; }
/* 4: why a recipe is out of reach, said plainly under it */
.craftrow .cwhy { color: #d08a6a; font-size: 11px; }
.craftrow .cmany { color: #7fd08a; font-size: 10px; font-weight: 400; margin-left: 6px; }
.totals { font-size: 12px; color: #a5a2b4; margin-bottom: 8px; }

/* the column count and the tile size are worked out in ui.js from the size
   of the bag and the size of the window - see fitGrid() */
#invgrid { display: grid; grid-template-columns: repeat(6, 40px); gap: 4px; margin: 8px 0; width: max-content; }
#equipgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.slot {
  position: relative; aspect-ratio: 1; background: #0f0e14; border: 1px solid #2b2937;
  border-radius: 4px; display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.slot:hover { border-color: #6a6480; }
.slot img { width: 84%; height: 84%; image-rendering: pixelated; }
.slot .qty { position: absolute; right: 2px; bottom: 0; font-size: 10px; color: #ffe08a; text-shadow: 0 1px 2px #000; }
.slot.eq { background: #17161f; }
/* THE TOOLS ARE NOT ARMOUR. They stand in their own column down the left with
   a tile of air and a rule between them and the doll, so the eye reads two
   groups rather than one lopsided one. */
.slot.eq.toolslot { margin-right: 18px; box-shadow: 20px 0 0 -19px #3a3448; }
/* the follow switch sits with the map's own buttons, not above them */
.mapfollow { display: inline-flex; align-items: center; gap: 3px; font-size: 11px;
  color: #8a86a0; cursor: pointer; user-select: none; }
.mapfollow input { margin: 0 1px 0 0; accent-color: #e8a04a; }
.sheetopt { display: flex; align-items: center; gap: 3px; font-size: 11px;
  color: #8a86a0; margin: 0 0 8px; cursor: pointer; width: fit-content; }
.sheetopt input { margin: 0 1px 0 0; }
.sheetopt input { accent-color: #e8a04a; }
.slot .slotname { position: absolute; left: 3px; top: 2px; font-size: 9px; color: #55526a; }
.gold { margin-top: 6px; font-size: 12px; color: #e8c14a; }

.skillrow { display: grid; grid-template-columns: 16px 74px 26px 1fr; gap: 6px; align-items: center; margin-bottom: 5px; font-size: 12px; }
/* each skill's little picture - the tool or weapon that trains it, and for
   Skinning its own pegged hide and knife (CORPSE.SKILL_ICON) */
.skillrow .sicon { width: 16px; height: 16px; image-rendering: pixelated; }
.skillrow .slvl { color: #fff; text-align: right; }
.skillrow .sbar { height: 8px; background: #0c0b10; border-radius: 3px; overflow: hidden; }
.skillrow .sbar i { display: block; height: 100%; }
.skillrow .sxp { grid-column: 4; font-size: 10px; color: #7d7a8c; }
/* what a melee, ranged or magic level buys - one line each, under the bar
   (see WHAT A FIGHTING LEVEL BUYS YOU in ui.js) */
.skillrow .sperk { grid-column: 1 / -1; margin-top: -3px; font-size: 10px; color: #a8a4b8; line-height: 1.25; }

/* THE KINDS OF WORK, as a row of little switches. Eighty-odd recipes in one
   scroll is a haystack; almost every trip to this panel is "I want to make a
   tool" or "I want armour", and that is one press away now. The count on each
   tab is how many of that kind you could make RIGHT NOW - so the answer to
   "is there anything I can do" is visible without opening anything. */
.crafttabs { display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 6px; }
.crafttab {
  padding: 3px 9px; font-size: 11px; border-radius: 4px; cursor: pointer;
  background: #1e1b28; border: 1px solid #3a3748; color: #cfc8bb;
}
.crafttab:hover { background: #262234; border-color: #4e4a60; }
.crafttab.on { background: #33291a; border-color: #8a7434; color: #ffe9b0; font-weight: 600; }
.crafttab i { font-style: normal; color: #7fd08a; margin-left: 5px; font-size: 10px; }
.crafttab.on i { color: #b8e8a8; }
/* the stations in reach, above the list of a window a station opened -
   see THE TITLE, AND THE OTHER STATIONS BESIDE YOU in ui.js */
.craftstations { display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 6px; }
.craftstations.hidden { display: none; }
.craftst {
  padding: 3px 10px; font-size: 11px; border-radius: 4px; cursor: pointer;
  background: #1e1b28; border: 1px solid #3a3748; color: #cfc8bb;
}
.craftst:hover { background: #262234; border-color: #4e4a60; }
.craftst.on { background: #2c3320; border-color: #7a9a44; color: #e8f6c8; font-weight: 600; }
.craftst.all { margin-left: auto; opacity: .85; }
.craftrow { display: flex; gap: 8px; align-items: center; padding: 6px; border-bottom: 1px solid #23212c; }
/* THE PIN NEVER FADES. A recipe you cannot make YET is exactly the one you
   most want pinned - that is what the little window is for - so it has to stay
   readable on a row that is telling you no.

   Opacity on the row itself could not be undone by a child: `opacity` makes a
   group, and everything inside it fades together no matter what a later rule
   says. So the row no longer fades - the picture and the words do, and the
   buttons are left alone. */
.craftrow.dim > img, .craftrow.dim > .ci { opacity: .45; }
.craftrow button:disabled { opacity: .5; }
/* craft any number - the box and its button side by side (2026-10-10) */
.craftrow .craftn { display: inline-flex; align-items: center; gap: 3px; }
.craftrow .craftn input { width: 44px; padding: 2px 3px; font: inherit; font-size: 12px; background: #14131a; color: #e9e4d6;
  border: 1px solid #3a3648; border-radius: 3px; }
.craftrow img { width: 28px; height: 28px; image-rendering: pixelated; }
.craftrow .ci { flex: 1; display: flex; flex-direction: column; font-size: 11px; }
/* THE TEXT NEVER GETS SQUEEZED TO A WORD A LINE (owner, 2026-10-15, screenshot of
   "Garlic Roast Boar"): with Pin, Craft, Craft 5x, Max, the count and Craft x all on
   one row, the recipe column shrank to ~40px and every input wrapped word by word.
   The text keeps a sensible minimum width and the buttons wrap onto their own line
   under it, right-aligned, when the row is too narrow for both. */
.craftrow { flex-wrap: wrap; }
.craftrow .ci { flex: 1 1 210px; min-width: 210px; }
.craftrow .ci + * { margin-left: auto; }
.craftrow .ci b { font-size: 12px; }
.craftrow em { font-style: normal; }
.craftrow em.ok { color: #7fd08a; }
.craftrow em.no { color: #d0455a; }
.craftrow .meta { color: #7d7a8c; font-size: 10px; }

.questrow { border-left: 2px solid #e8a04a; padding: 6px 8px; margin-bottom: 10px; background: #17161f; }
.questrow b { color: #ffe08a; }
.questrow p { color: #a5a2b4; font-size: 12px; margin: 4px 0; }
.qstep { font-size: 12px; color: #c9c6d6; display: flex; justify-content: space-between; }
.qstep.done { color: #5ec86a; text-decoration: line-through; }
/* the quest card's button: bottom-right, and Accept / Turn in in yellow with
   1px larger, bold text so it is the first thing the eye finds (owner, 2026-10-15) */
.questrow .qbtn { display: block; margin: 8px 0 0 auto; }
.questrow .qaccept, .questrow .qturnin {
  background: #e8c14a; color: #1b1608; border-color: #f6dc84;
  font-size: 13px; font-weight: 700; padding: 6px 14px;
}
.questrow .qaccept:hover, .questrow .qturnin:hover { background: #f4d468; border-color: #fff0b0; }
.ready { color: #5ec86a; font-size: 10px; }

.buildrow { display: flex; gap: 8px; align-items: center; padding: 5px; border: 1px solid #23212c; border-radius: 4px; margin-bottom: 4px; cursor: pointer; }
.buildrow:hover { border-color: #6a6480; }
.buildrow.sel { border-color: #e8a04a; background: #201c19; }
.buildrow img { width: 24px; height: 24px; image-rendering: pixelated; }
/* the vault lays itself out to the window in ui.js - no scrollbar */
.depgrid.vault { grid-template-columns: repeat(12, 1fr); }
/* the vault is the one window you stare into for a while - give it room */
.panel[data-name="bank"] { width: 620px; }
.panel[data-name="bank"] .depgrid { grid-template-columns: repeat(12, 1fr); }
.panel[data-name="bank"] .depgrid.bagrow { grid-template-columns: repeat(10, 1fr); }
/* WHAT YOU SEARCHED FOR. A green ring round every copy of it, in the bag, in
   an opened bag and in the vault. Nothing else changes: searching for a thing
   you have not got leaves the bag looking exactly as it did, rather than
   greying everything out to tell you so. */
/* SEARCHING LIGHTS IT UP (ui.js, owner 2026-10-05): the ring is a clear one
   now - brighter, wider, glowing past the tile's edge, with a slow breath so
   it is found across a full vault - and a list lights the word itself */
.slot.found {
  box-shadow: 0 0 0 2px #7cf08a inset, 0 0 0 1px #7cf08a, 0 0 12px 2px #5ec86aaa;
  background-color: #1f3a24; animation: srchfound 1.6s ease-in-out infinite;
}
@keyframes srchfound { 50% { box-shadow: 0 0 0 2px #b4ffbe inset, 0 0 0 1px #b4ffbe, 0 0 16px 4px #5ec86acc; } }
mark.srchhit { background: #f2c94c; color: #1a1420; border-radius: 2px; padding: 0 1px; box-shadow: 0 0 6px #f2c94c88; }
@media (prefers-reduced-motion: reduce) { .slot.found { animation: none; } }
.slot.faded { opacity: .3; }
#housebox { margin-top: 8px; }
#btn-house { width: 100%; margin-top: 6px; background: #2a3a24; border-color: #4a6a3a; }
#battlebox {
  /* the minimap owns the top-right corner, and the side buttons the strip
     under it - sit to the left of both. Draggable, and it remembers. */
  position: absolute; right: 64px; top: 206px; width: 210px;
  background: #12111ac8; border: 1px solid #2b2937; border-radius: 6px; padding: 6px;
}
#battlebox .bhead {
  font-size: 10px; letter-spacing: 1px; text-transform: uppercase; color: #7d7a8c;
  margin-bottom: 4px; cursor: move; user-select: none;
  display: flex; justify-content: space-between; align-items: baseline; gap: 6px;
}
#battlelist { max-height: 210px; overflow: auto; }
/* THREE COLUMNS: the level, the name, how far off it is - and the health bar
   under all three. A level chip was added to a two-column grid and pushed the
   distance onto a line of its own, which is what made it look broken. */
.battlerow {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: 5px; row-gap: 3px; align-items: center;
  padding: 3px 5px; margin: 1px 0; border-radius: 3px; cursor: pointer; font-size: 11px;
}
.battlerow:hover { background: #23212c; }
.battlerow.on { background: #3a2418; box-shadow: inset 0 0 0 1px #e8a04a; }
.battlerow .bn { grid-column: 2; color: #e8e6ef; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.battlerow .bd { grid-column: 3; color: #7d7a8c; text-align: right; font-variant-numeric: tabular-nums; }
.battlerow .bhp { grid-column: 1 / -1; height: 3px; background: #0c0b10; border-radius: 2px; overflow: hidden; }
.battlerow .bhp i { display: block; height: 100%; background: #c8455a; }
/* WHAT YOU ARE WEARING, LAID OUT AS A PERSON.
   Five columns and four rows at the top of the Character window:

        light    melee     HEAD     shield   chain
          .      ranged    CHEST    gloves     .
          .      magic     LEGS       .        .
          .      ring 1    BOOTS    ring 2     .

   Armour down the middle from the head to the feet, the three weapons down
   the left with a ring under them, shield and the other ring to the right,
   the lamp out west and the chain out east. */
#equipgrid {
  display: grid !important;
  grid-template-columns: repeat(5, 46px) !important;
  grid-template-rows: repeat(4, 46px);
  gap: 4px; width: max-content; margin: 2px auto 10px;
}
#equipgrid .slot { grid-area: var(--at); }
/* ---- the dyer's table ----------------------------------------------------
   A worktop seen from above. The ring is a 4x4 grid whose middle 2x2 is given
   over to the vat, which is why there are exactly twelve wells: sixteen cells
   less the four the bag sits on. The tools are CSS rather than sprites -
   they never move and never need a cache. */
#dyetable {
  position: relative; width: 260px; height: 260px; margin: 6px auto 10px;
  border-radius: 6px;
  background:
    repeating-linear-gradient(180deg, #6a4a2e 0 22px, #5f4228 22px 24px),
    #6a4a2e;
  box-shadow: inset 0 0 0 2px #4a3020, inset 0 0 26px #00000055;
}
#dyetable .dring {
  /* THE BOARDS SHOW ROUND THE RING. The inset was 26px and the tools drawn in
     that margin came out clipped and unreadable - a brush four pixels wide
     rotated inside a twenty-six pixel band is a smudge. A wider margin is the
     whole fix: it is the table top, and the table top is where the trade's
     tools are supposed to be lying. */
  position: absolute; inset: 40px;
  display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, 1fr);
  gap: 6px;
}
/* a well: a clay dish sunk into the boards */
#dyetable .dwell {
  position: relative; border-radius: 50%;
  background: #43301f; box-shadow: inset 0 2px 4px #00000088, 0 1px 0 #7a5433;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
}
#dyetable .dwell:hover { box-shadow: inset 0 2px 4px #00000088, 0 0 0 2px #9a7a52; }
#dyetable .dwell img { width: 76%; height: 76%; image-rendering: pixelated; }
#dyetable .dwell.wet { box-shadow: inset 0 2px 4px #00000088, 0 0 8px -1px var(--wc, #fff); }
/* the vat: the middle four cells, sunk deeper than the wells */
#dyetable .dvat {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 88px; height: 88px; border-radius: 8px;
  background: #2e2016; box-shadow: inset 0 3px 8px #000000aa, 0 0 0 2px #4a3020;
  display: flex; align-items: center; justify-content: center;
}
#dyetable .dvat canvas { width: 64px; height: 64px; image-rendering: pixelated; }
#dyetable .dvat.ready { box-shadow: inset 0 3px 8px #000000aa, 0 0 0 2px #4a3020, 0 0 14px -2px var(--vc, #fff); }
/* the tools, laid on the boards where nothing else goes */
#dyetable .dtools i { position: absolute; display: block; }
/* two brushes laid across the top-left corner, bristles still wet and each
   wet with a different colour - which is the one detail that says what the
   table is for without a word on it */
#dyetable .dt-brush  { left: 10px; top: 6px;  width: 6px; height: 46px; border-radius: 3px;
  background: linear-gradient(#8a6440 0 8%, #6a4a2e 8% 66%, #e8a83c 66% 100%);
  transform: rotate(-22deg); box-shadow: 1px 2px 3px #00000066; }
#dyetable .dt-brush2 { left: 26px; top: 10px; width: 6px; height: 40px; border-radius: 3px;
  background: linear-gradient(#8a6440 0 8%, #6a4a2e 8% 64%, #7fd08a 64% 100%);
  transform: rotate(12deg); box-shadow: 1px 2px 3px #00000066; }
/* a pestle standing in its mortar */
#dyetable .dt-pestle { right: 12px; top: 8px; width: 30px; height: 20px;
  background: #8a8a96; border-radius: 4px 4px 14px 14px;
  box-shadow: inset 0 -5px 0 #6a6a76, inset 0 3px 0 #a8a8b4, 1px 2px 3px #00000066; }
#dyetable .dt-pestle::after { content: ''; position: absolute; right: 4px; top: -12px;
  width: 5px; height: 16px; background: #6a4a2e; border-radius: 2px; transform: rotate(16deg); }
/* and a stained rag over the near edge */
#dyetable .dt-rag { right: 14px; bottom: 8px; width: 38px; height: 18px;
  background: linear-gradient(#e2e5ec 0 34%, #c6c9d2 34% 100%); border-radius: 3px;
  transform: rotate(-6deg); box-shadow: 1px 2px 3px #00000066; }
#dyetable .dt-rag::after { content: ''; position: absolute; left: 6px; top: 7px;
  width: 12px; height: 7px; background: #6a2a4a99; border-radius: 4px; }
#dyebench .depgrid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px; }
/* a kind you have put all of on the table: still there, still shows its
   count at zero, but plainly not something you can click again */
#dyebench .slot.spent { opacity: .38; cursor: default; }
#dyebench .slot.spent:hover { border-color: #2b2937; }
#dyebench .slot .qty { color: #cfe0a8; }


/* ---- the gear level, under the doll -------------------------------------
   One row: a quiet label, a big number in the tier's colour, the tier's name,
   and a bar underneath that is the same fact as a shape. The colour arrives
   as --gl from refreshGearLevel; nothing here knows the six tiers by name,
   which is what keeps the ramp defined in exactly one place. */
#gearlevel {
  --gl: #8e8e98;
  width: max-content; max-width: 100%;
  margin: -4px auto 10px; padding: 5px 12px 6px;
  display: grid; grid-template-columns: auto auto auto; align-items: baseline;
  column-gap: 7px; row-gap: 4px;
  border: 1px solid color-mix(in srgb, var(--gl) 38%, transparent);
  border-radius: 5px;
  background: color-mix(in srgb, var(--gl) 9%, transparent);
  cursor: help;
}
#gearlevel .gllabel {
  font-size: 10px; letter-spacing: 1.1px; text-transform: uppercase;
  color: #8b889b;
}
#gearlevel .glnum {
  font-size: 19px; line-height: 1; font-weight: 700;
  color: var(--gl); font-variant-numeric: tabular-nums;
}
#gearlevel .glrank {
  font-size: 11px; letter-spacing: 0.4px;
  color: color-mix(in srgb, var(--gl) 72%, #cfccdd);
}
#gearlevel .glbar {
  grid-column: 1 / -1;
  height: 3px; border-radius: 2px; overflow: hidden;
  background: #2a2833;
}
#gearlevel .glbar i {
  display: block; height: 100%; border-radius: 2px;
  background: var(--gl);
  transition: width 220ms ease;
}
/* the top of the ladder gets the one flourish in here, because reaching it is
   the only thing on this line that is not routine */
#gearlevel.full {
  border-color: var(--gl);
  box-shadow: 0 0 10px -2px color-mix(in srgb, var(--gl) 55%, transparent);
}
#gearlevel.full .glnum { text-shadow: 0 0 8px color-mix(in srgb, var(--gl) 60%, transparent); }
@media (prefers-reduced-motion: reduce) { #gearlevel .glbar i { transition: none; } }
.bagbelt { width: 288px; max-width: 100%; margin: 0 auto 8px; }
/* and the bag itself is centred in its own window */
#invgrid { margin-left: auto; margin-right: auto; }
.keys { padding-left: 16px; font-size: 12px; color: #c9c6d6; line-height: 1.8; }

/* rolled-up windows keep only their title bar */
.panel h3 .min { padding: 1px 7px; font-size: 12px; margin-left: auto; margin-right: 4px; }
.panel.rolled > *:not(h3) { display: none; }
.panel.rolled { width: auto !important; min-width: 190px; }
.panel h4.sub { margin: 10px 0 6px; font-size: 11px; color: #7d7a8c; text-transform: uppercase; letter-spacing: 1px; }

/* The five-column skill LIST used to be styled here - #treelist as a grid,
   .treebranch and nine .treenode rules. The chart replaced it and nothing in
   the game asks for any of them any more. Kept once on the grounds that they
   cost nothing; they cost the next person reading this file the time it takes
   to work out which of two skill-tree stylesheets is the live one, which is
   more than nothing. Deleted. */

/* --------------------------------------------------------- its bowl -----
   On the plate, beside the buff line. Small, because the plate is small, and
   it has to read as a container whether or not there is anything in it. */
.catrow { display: flex; align-items: center; gap: 6px; }
.catrow .line2 { flex: 1; min-width: 0; }
.foodslot {
  position: relative; width: 24px; height: 24px; flex: 0 0 24px;
  border: 1px solid #3a3547; border-radius: 4px; background: #14121b;
  display: flex; align-items: center; justify-content: center;
}
.foodslot.full { border-color: #6a5f3a; background: #1b1810; cursor: pointer; }
.foodslot.droptarget { border-color: #e8a04a; background: #2a2114; }
.foodslot img { width: 18px; height: 18px; image-rendering: pixelated; }
.foodslot .qty {
  position: absolute; right: 0; bottom: -2px; font-size: 9px; color: #ffe08a;
  text-shadow: 0 1px 0 #000;
}
/* an empty bowl says so rather than looking like a missing icon */
.foodslot:not(.full)::after {
  content: ''; width: 10px; height: 5px; border: 1px solid #3a3547;
  border-top: 0; border-radius: 0 0 5px 5px;
}

/* -------------------------------------------------------- the altar -----
   A basin seen from above with the light sitting in it: the same idea as the
   rune slab, so the two stations read as the same kind of thing. */
/* ============ THE ALTAR, CUT TO MATCH ================================
   Same workshop as the star-chart and the gem gate: deep blue-black ground,
   a silver rule, corner marks, and capitals with air in them. The basin was
   a rounded grey box with a blue smear in it; it is a stone bowl in a
   frame now, and the light in it reads as light rather than as a gradient. */
#altarbasin {
  position: relative; height: 92px; margin: 8px 0 12px; border-radius: 2px;
  background: #0a0e1c radial-gradient(ellipse 70% 90% at 50% 62%, #16223f 0%, #0a0e1c 64%, #05070f 100%);
  border: 1px solid rgba(200, 212, 232, .24); overflow: hidden;
}
#altarbasin::before, #altarbasin::after {
  content: ''; position: absolute; width: 11px; height: 11px; pointer-events: none;
  border: 1px solid rgba(200, 212, 232, .38);
}
#altarbasin::before { left: 5px; top: 5px; border-right: 0; border-bottom: 0; }
#altarbasin::after { right: 5px; bottom: 5px; border-left: 0; border-top: 0; }
#altarbasin .ab-bowl {
  position: absolute; left: 50%; top: 50%; width: 62px; height: 30px;
  transform: translate(-50%, -50%);
  border: 2px solid #4a4560; border-radius: 50%; background: #0d0b14;
}
#altarbasin .ab-glow {
  position: absolute; left: 50%; top: 50%; width: 40px; height: 18px;
  transform: translate(-50%, -50%);
  border-radius: 50%; background: #9fd8ff; opacity: .18;
  animation: abglow 3.2s ease-in-out infinite;
}
@keyframes abglow { 0%, 100% { opacity: .10; } 50% { opacity: .34; } }
@media (prefers-reduced-motion: reduce) { #altarbasin .ab-glow { animation: none; } }

.altarline {
  display: flex; align-items: center; gap: 9px;
  font: 12px/1.5 ui-monospace, Consolas, monospace;
  padding: 6px 7px; border-bottom: 1px solid rgba(200, 212, 232, .12);
}
.altarline img { width: 20px; height: 20px; image-rendering: pixelated; }
.altarline .al-name { flex: 1; color: #c8d4e8; letter-spacing: 1px; }
/* what you have against what it wants, lined up so the eye can subtract */
.altarline .al-num { color: #9fe6b0; font-variant-numeric: tabular-nums; letter-spacing: 1px; }
.altarline.short .al-num { color: #e07a6c; }
#altarwho {
  font: 12px/1.55 ui-monospace, Consolas, monospace;
  color: #c8d4e8; letter-spacing: 1px;
}
#btn-revive {
  font: 600 11px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 3px; text-transform: uppercase;
  padding: 7px 20px; background: #131a2e; color: #fff6d8;
  border: 1px solid rgba(200, 212, 232, .38); border-radius: 2px;
}
#btn-revive:hover:not(:disabled) { background: #1b2440; border-color: #fff6d8; }
#btn-revive:disabled { color: #7c8aa8; border-color: rgba(200, 212, 232, .16); }

/* --------------------------------------------------- the star-chart -----
   The chart replaces the five columns. Everything here carries an sc- prefix
   because this stylesheet is one flat namespace shared with every other panel
   in the game, and a bare .tile or .star would have collided within a week.
   The list stylesheet that stood here before it has been removed rather than
   left dormant. */
#treelist.chart {
  display: block; max-height: none; overflow: visible; padding: 10px;
  background: #070a14 radial-gradient(ellipse 90% 60% at 50% 44%, #0c1328 0%, #070a14 58%, #04060d 100%);
  border: 1px solid rgba(200, 212, 232, .22); line-height: 0;
}
/* ============ WHAT YOU HAVE LEARNED, BESIDE THE CHART ====================
   Owner, 2026-10-05: "The list of selected skill tree points should be
   listed to the left of the skilltree, not in the character sheet." A
   column in the chart's own idiom - silver on near-black, monospace - as
   tall as the chart and scrolling inside itself, so a long list never
   pushes the chart about. In a narrow window it goes above the chart
   instead of squeezing it. Built by refreshTreeChosen in ui.js. */
.treewrap { display: flex; gap: 8px; align-items: stretch; }
.treewrap > #treelist { flex: 1 1 auto; min-width: 0; }
#treechosen {
  flex: 0 0 clamp(150px, 22%, 205px); position: relative; min-height: 120px;
  background: #0a0e1c; border: 1px solid rgba(200, 212, 232, .22); color: #c8d4e8;
  font: 11px/1.4 ui-monospace, Consolas, "Lucida Console", monospace;
}
#treechosen .tc-in { position: absolute; inset: 0; overflow-y: auto; padding: 8px 8px 10px; }
#treechosen .tc-head { letter-spacing: 2px; color: #7c8aa8; font-size: 10px; margin-bottom: 6px; display: flex; justify-content: space-between; }
#treechosen .tc-br { margin: 8px 0 3px; font-size: 10px; letter-spacing: 2px; }
#treechosen .tc-row { padding: 3px 0 4px; border-bottom: 1px solid rgba(200, 212, 232, .08); cursor: default; }
#treechosen .tc-row:hover { background: rgba(200, 212, 232, .06); }
#treechosen .tc-top { display: flex; justify-content: space-between; gap: 6px; }
#treechosen .tc-name { color: #e6ecf8; overflow-wrap: anywhere; }
#treechosen .tc-rank { color: #fff6d8; white-space: nowrap; font-variant-numeric: tabular-nums; }
#treechosen .tc-desc { color: #8f9bb6; font-size: 10px; overflow-wrap: anywhere; }
#treechosen .tc-none { color: #7c8aa8; }
@media (max-width: 760px) {
  .treewrap { flex-direction: column; }
  #treechosen { flex: none; height: 150px; }
}
/* the chart is an 840-wide drawing scaled to whatever the panel is; every
   coordinate in it is rounded to a whole unit and drawn with crispEdges, so
   it stays a pixel picture rather than turning into soft vector art */
.sc-svg { display: block; width: 100%; height: auto; }
/* THE BIG CHART (CONFIG.SKILL_TREE2, ui.js THE CHART YOU MOVE AROUND IN):
   drawn at its own size inside a window onto it you drag, scroll or arrow
   around - never shrunk. The spec row above and the reading card below stay
   where they are. */
#treelist.chart:has(.sc2-view) { padding: 0; position: relative; line-height: normal; }
/* the wheel zooms and a drag pans (ui.js) - no scrollbars; zoomed all the
   way out the chart sits in the middle of the window */
.sc2-view {
  height: min(560px, calc(100vh / var(--uiz, 1) - 330px)); min-height: 260px;
  overflow: hidden; cursor: grab; outline: none; overscroll-behavior: contain;
  background: #070a14; display: flex;
}
.sc2-view:focus-visible { box-shadow: inset 0 0 0 1px rgba(255, 246, 216, .5); }
.sc2-view.sc2-grab { cursor: grabbing; }
.sc2-view .sc-nd.sc-st-open { cursor: pointer; }
.sc2-svg { display: block; max-width: none; flex: none; margin: auto; }
/* the name, only under the pointer */
.sc2-tip {
  position: absolute; transform: translate(-50%, -100%); pointer-events: none; z-index: 2;
  padding: 3px 8px; white-space: nowrap; background: #0a0e1c; border: 1px solid rgba(200, 212, 232, .35);
  color: #c8d4e8; font: 11px/1.3 ui-monospace, Consolas, "Lucida Console", monospace; letter-spacing: 1px;
}
.sc2-tip[hidden] { display: none; }
.sc2-hint { position: absolute; right: 8px; bottom: 6px; pointer-events: none; color: #7c8aa8; opacity: .7;
  font: 10px ui-monospace, Consolas, "Lucida Console", monospace; letter-spacing: 1px; }
.sc-txt { font-family: ui-monospace, Consolas, "Lucida Console", monospace; }
.sc-nd { cursor: default; outline: none; }
.sc-nd.sc-st-open { cursor: pointer; }
/* the star under the pointer lifts its rim and puts its name up rather than
   moving - a chart where things jump about under the cursor is hard to aim at */
.sc-nd:hover .sc-tile, .sc-nd:focus .sc-tile { stroke-width: 2px; }
.sc-nd:focus .sc-tile { stroke: #fff6d8; }
.sc-name { opacity: 0; }
.sc-nd:hover .sc-name, .sc-nd:focus .sc-name { opacity: 1; }
.sc-nd:hover .sc-rank, .sc-nd:focus .sc-rank { opacity: 1; }

/* THE BREATH IS TWO MOVEMENTS AT ONCE, which is what makes it read as a star
   rather than a blinking light: the halo swells outward and fades in while
   the core brightens inward on the same beat. */
.sc-halo-anim {
  transform-box: fill-box; transform-origin: center;
  animation: sc-breathe 2.6s ease-in-out infinite;
}
@keyframes sc-breathe {
  0%, 100% { transform: scale(1); opacity: .45; }
  50% { transform: scale(1.22); opacity: 1; }
}
.sc-core-anim { animation: sc-corepulse 2.6s ease-in-out infinite; }
@keyframes sc-corepulse { 0%, 100% { opacity: .2; } 50% { opacity: 1; } }
/* light running outward along a thread: a short lit dash on a long gap, so
   what you see is one pip travelling rather than a moving dashed line */
.sc-pulse { animation: sc-dashflow 1.7s linear infinite; }
@keyframes sc-dashflow { from { stroke-dashoffset: 0; } to { stroke-dashoffset: -64; } }
.sc-twk { animation: sc-twinkle 3.4s ease-in-out infinite; }
@keyframes sc-twinkle { 0%, 100% { opacity: .12; } 50% { opacity: .7; } }
.sc-hubstar { animation: sc-hubglow 4.2s ease-in-out infinite; }
@keyframes sc-hubglow { 0%, 100% { opacity: .7; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .sc-halo-anim, .sc-core-anim, .sc-pulse, .sc-twk, .sc-hubstar { animation: none !important; }
  /* still, but not invisible - the halo has to keep marking what is affordable */
  .sc-halo-anim { opacity: .8; }
}

/* the reading card, in the chart's own idiom rather than the old panel's:
   silver rules on near-black, with a corner bracket at each end the way an
   astrolabe plate is cornered */
#treeinfo {
  position: relative; margin-top: 10px; min-height: 78px; padding: 12px 18px;
  box-sizing: border-box; font-size: 12px; line-height: 1.5;
  background: #0a0e1c; border: 1px solid rgba(200, 212, 232, .25); color: #c8d4e8;
  font-family: ui-monospace, Consolas, "Lucida Console", monospace;
}
#treeinfo::before, #treeinfo::after,
#treeinfo .sc-cn-tr, #treeinfo .sc-cn-bl {
  content: ''; position: absolute; width: 9px; height: 9px; pointer-events: none;
}
#treeinfo::before { top: 3px; left: 3px;
  border-top: 1px solid rgba(200, 212, 232, .5); border-left: 1px solid rgba(200, 212, 232, .5); }
#treeinfo::after { bottom: 3px; right: 3px;
  border-bottom: 1px solid rgba(200, 212, 232, .5); border-right: 1px solid rgba(200, 212, 232, .5); }
#treeinfo .sc-cn-tr { top: 3px; right: 3px;
  border-top: 1px solid rgba(200, 212, 232, .5); border-right: 1px solid rgba(200, 212, 232, .5); }
#treeinfo .sc-cn-bl { bottom: 3px; left: 3px;
  border-bottom: 1px solid rgba(200, 212, 232, .5); border-left: 1px solid rgba(200, 212, 232, .5); }
.sc-cd-top { display: flex; justify-content: space-between; align-items: baseline;
             letter-spacing: 2px; font-size: 12px; gap: 10px; }
.sc-cd-name { font-weight: bold; }
.sc-cd-glyph { vertical-align: -3px; margin-right: 7px; }
.sc-cd-rank { color: #7c8aa8; font-size: 11px; letter-spacing: 1px; white-space: nowrap; }
.sc-cd-desc { color: #c8d4e8; opacity: .85; margin-top: 4px; }
.sc-cd-status { margin-top: 6px; font-size: 11px; letter-spacing: 1px; }
/* one empty box per rank, filled up to where you are - the same reading as
   the level rising inside the star, for the one node you are looking at */
.sc-pip {
  display: inline-block; width: 6px; height: 6px; margin-left: 3px;
  border: 1px solid rgba(124, 138, 168, .55); vertical-align: middle; box-sizing: border-box;
}

/* ============ THE SPEC ROW ================================================
   Owner, 2026-09-29: "You can have different skillspecs in the skilltree,
   but it cost 1000g to change each time." A row of tabs over the chart in
   the chart's own idiom - silver on near-black, the spec in use lit in the
   star-chart's gold - and the price written on the Switch button itself.
   See THE SPEC ROW in ui.js. */
#specrow { margin-bottom: 8px; }
#specrow:empty { display: none; }
.spec-tabs { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.spec-tab {
  padding: 4px 12px; font-size: 12px; letter-spacing: 1px; border-radius: 0;
  font-family: ui-monospace, Consolas, "Lucida Console", monospace;
  background: #0a0e1c; color: #7c8aa8; border: 1px solid rgba(200, 212, 232, .22);
}
.spec-tab small { margin-left: 8px; font-size: 10px; opacity: .7; }
.spec-tab:hover { background: #0d1526; color: #c8d4e8; border-color: rgba(200, 212, 232, .45); }
.spec-tab.look { background: #0d1526; color: #c8d4e8; border-color: rgba(200, 212, 232, .6); }
.spec-tab.on { color: #e8c46a; border-color: rgba(232, 196, 106, .7); box-shadow: inset 0 -2px 0 #e8c46a; }
.spec-tab.on::before { content: '\2726'; margin-right: 6px; }
.spec-tab.over small { color: #d0455a; opacity: 1; }
.spec-acts { margin-left: auto; display: flex; gap: 4px; }
.spec-acts button { padding: 4px 10px; font-size: 11px; }
.spec-switch:not(:disabled) { color: #e8c46a; border-color: rgba(232, 196, 106, .6); }
.spec-note { margin-top: 5px; font-size: 11px; color: #7c8aa8; }
.spec-note b { color: #e8c46a; font-weight: normal; }

/* bank */
.bankrow { display: flex; gap: 8px; align-items: center; padding: 4px 6px; border-bottom: 1px solid #23212c; font-size: 12px; }
.bankrow img { width: 22px; height: 22px; image-rendering: pixelated; }
.bankrow span { flex: 1; }
.bankrow button { padding: 2px 8px; font-size: 11px; }
.depgrid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px; }
.shoprow { display: flex; gap: 8px; align-items: center; margin: 5px 0; font-size: 12px; }
.shoprow img { width: 22px; height: 22px; image-rendering: pixelated; }
.shoprow span { flex: 1; }
/* the stat line under a piece of gear on the counter (owner, 2026-10-05) */
.shoprow .shopstats { display: block; font-size: 11px; margin-top: 1px; }
.shoprow button { padding: 2px 8px; font-size: 11px; }

/* ------------------------------------------------------------ game menu */
#menu { width: 560px; }
#menu h4.sub { margin: 14px 0 8px; font-size: 11px; color: #7d7a8c; text-transform: uppercase; letter-spacing: 1px; }
/* ============ EVERY BUTTON IN HERE IS FULL WIDTH, EXCEPT THE X ========
   The menu's buttons are a stack of full-width rows, and this rule makes
   them so. It also caught the close button that cornerX stamps into every
   window as its FIRST child - `#menu > button` is one id and one type, which
   beats a lone `.winx` class - and stretched it to the full 560px. That is
   the enormous X across the top of the menu: not a button anybody wrote
   there, a corner button that this line inflated.

   The `.winx` rule already knows what it wants to be - sticky, floated
   right, 26 by 26 - and gets it everywhere else. Here it just has to be let
   alone. */
#menu > button:not(.winx) { display: block; width: 100%; margin-top: 6px; }
/* The heading stays put while the menu scrolls, so the title and the corner
   x are in the same place whether you are looking at the sliders or the
   hotkeys. -18px cancels the modal's own top padding so it parks flush with
   the top edge rather than floating a gap below it. The x itself is .winx
   and needs nothing here - it only needed to stop being stretched. */
#menu h2 { position: sticky; top: -18px; z-index: 3; }
/* ============ AND THE X SITS IN THE BAR, NOT ON ITS EDGE ==============
   .winx is placed for an ordinary window, whose heading is a line of text.
   The menu's heading is a full-bleed bar with a rule under it, and the
   button landed five pixels PAST that rule - resting on the line like
   something that had slipped.

   Measured rather than nudged: the bar runs 1 to 50 from the top of the
   window and the button is 26 tall, so centred is 13. These two numbers put
   it there - 12 above, 11 below - and it stays there at both ends of the
   scroll, which is what the sticky offset is for. */
#menu > .winx { margin-top: -16px; top: -6px; }
/* ============ THE MENU, IN CATEGORIES (owner, 2026-10-10) =============
   "organize all the menu items in the escape menu to categories". A fixed
   height, so the window does not jump in size from tab to tab; the tabs and
   the footer (Back / Log out / Exit) stay put and only the pane scrolls.
   The box no longer scrolls itself, so the corner x is simply pinned. */
#menu.tabbed { width: 640px; height: min(640px, calc(84vh / var(--uiz, 1))); max-height: calc(84vh / var(--uiz, 1)); display: flex; flex-direction: column; overflow: hidden; padding-bottom: 12px; }
#menu.tabbed > h2 { position: relative; top: 0; flex: none; }
#menu.tabbed > .winx { position: absolute; top: 12px; right: 10px; margin: 0; float: none; }
#menu .menutabs { flex: none; display: flex; flex-wrap: wrap; gap: 3px; margin: 0 0 12px; border-bottom: 1px solid #4a3a28; }
#menu .menutab { margin: 0 0 -1px; padding: 7px 14px; font-size: 12.5px; letter-spacing: .4px; color: #a9a4b8;
  background: #1b1a24; border: 1px solid #2e2b3a; border-bottom-color: #4a3a28; border-radius: 6px 6px 0 0; }
#menu .menutab:hover { color: #ffd9a0; background: #241f2c; }
#menu .menutab.on { color: #ffd9a0; background: #3a2c1c; border-color: #e8a04a; border-bottom-color: #3a2c1c; font-weight: 600; }
#menu .menupane { flex: 1 1 auto; min-height: 0; overflow: hidden auto; padding-right: 6px; }
#menu .menupane > h4.sub:first-child { margin-top: 2px; }
#menu .menupane > button { display: block; width: 100%; margin-top: 6px; }
#menu .menufoot { flex: none; display: flex; gap: 8px; margin-top: 10px; padding-top: 10px; border-top: 1px solid #2e2b3a; }
#menu .menufoot button { flex: 1; }
#menu .menufoot button.del { border-color: #7a3030; color: #ffb0a8; }
#menu .menufoot button.del:hover { background: #3a1c1c; border-color: #e0453f; }
/* the hotkeys: the pane is the one scroll, not a box inside a box */
#menu.tabbed .keygrid { max-height: none; overflow: visible; padding-right: 0; }
#menu .keyrow { gap: 6px; }
#menu .keycaps { min-width: 150px; text-align: right; }
#menu .keycap { display: inline-block; min-width: 22px; padding: 2px 7px; text-align: center; font-size: 11.5px; color: #ffe0b0;
  background: #241f2c; border: 1px solid #4a4055; border-bottom-width: 2px; border-radius: 4px; }
#menu .keycap.spare { color: #c9c6d6; opacity: .85; }
#menu .keycap.none { color: #7d7a8c; background: transparent; border-style: dashed; }
#menu .keycap.warn { border-color: #c94b3a; color: #ffb4a6; }
#menu .keycap.listening { color: #ffe08a; background: #7a3a1a; border-color: #e8a04a; }
#menu .keycap.listening i { font-style: normal; color: #ffd9a0; opacity: .8; }
#menu .keyrow .keybtn { min-width: 66px; padding: 4px 8px; }
#menu .keyrow .keybtn.spare { min-width: 66px; opacity: 1; color: #a9a4b8; }
#menu .setrow select { width: auto; min-width: 150px; }
/* a note beside a switch (the piece playing, what stops a chase) gets room to read on one or two lines */
#menu .setrow > button ~ .setval { width: auto; min-width: 44px; max-width: 250px; text-align: left; font-size: 11px; }
#menu .keyrow .keybtn.listening { background: #7a3a1a; border-color: #e8a04a; color: #ffe08a; }
#menu .keytips { margin: 0 0 8px; font-size: 11.5px; }
#menu .keytips summary { cursor: pointer; color: #c9a227; margin-bottom: 6px; }
#menu .keyresetall { margin-top: 10px; }

/* ============ WHAT YOU ARE HITTING ====================================
   The battle list on the right says what is in range; it does not answer
   "how is THIS one doing" without reading down a list of rows while
   something is swinging at you. This is the one you picked, alone, with its
   name written across its own health - so the answer is where your eyes
   already are rather than off at the edge of the screen.

   Top centre: the plates own the top left, the minimap and the battle list
   the top right, and the middle of the screen above the action was empty. */
#targetframe {
  position: absolute; left: 50%; transform: translateX(-50%); top: 12px;
  width: 260px; z-index: 5;
}
#targetframe .bar {
  height: 22px; margin: 0; background: #0c0b10;
  border: 1px solid #4a2b33; border-radius: 4px;
  box-shadow: 0 2px 10px #0009;
}
#targetframe .bar i { background: linear-gradient(#d0455a, #8e2b3a); }
#targetframe .bar span {
  line-height: 20px; font-size: 12px; color: #f2e9ea;
  text-shadow: 0 1px 3px #000, 0 0 6px #000;
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
/* NO CHIP BEHIND THE LEVEL. It had a black wash behind it to hold the number
   off the bar, and over a red bar that wash reads as a patch of darker red -
   a stripe that looks like part of the health rather than part of the label.
   The number is bright enough on its own; the shadow it inherits from the
   row above does the separating. */
#targetframe .tlv {
  font-size: 11px; color: #ffd9a0; font-variant-numeric: tabular-nums;
}
#targetframe .thp { font-size: 10px; color: #e8dcdd; font-variant-numeric: tabular-nums; opacity: .85; }
.setrow { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: 12px; }
.setname { flex: 1; color: #c9c6d6; }
.setrow input[type=range] { width: 200px; accent-color: #e8a04a; }
.setval { width: 44px; text-align: right; color: #7d7a8c; }
/* the display rows: which of two is on, and a line saying what it means */
.setrow button.chosen { border-color: #e8a04a; color: #ffd9a0; background: #3a2c1c; }
.setnote { margin: -2px 0 8px; font-size: 11px; }
.keygrid { max-height: calc(34vh / var(--uiz, 1)); overflow: auto; padding-right: 4px; }
.keygrid .setrow { margin-bottom: 3px; }
.keybtn { min-width: 92px; font-family: inherit; }
.keybtn.listening { background: #7a3a1a; border-color: #e8a04a; color: #ffe08a; }
/* every key on the board, grouped (owner 2026-09-29: "all hotkeys ... rebindable") */
#menu .keygrid { max-height: calc(46vh / var(--uiz, 1)); }
.keyhead { margin: 10px 0 4px; padding-bottom: 2px; font-size: 10.5px; color: #c9a227; text-transform: uppercase; letter-spacing: 1px; border-bottom: 1px solid #3a3040; }
.keyhead:first-child { margin-top: 0; }
.keyrow .keybtn { min-width: 84px; }
.keybtn.spare { min-width: 70px; opacity: 0.85; }
.keybtn.fixed { opacity: 0.55; cursor: default; }
.keybtn.warn { border-color: #c94b3a; color: #ffb4a6; }
.keyreset { min-width: 26px; padding: 2px 6px; }
.keyreset.same { opacity: 0.25; }
.keynote { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 2px 0 8px; padding: 6px 8px; font-size: 11.5px; color: #ffe0b0; background: #2a1f14; border: 1px solid #7a5a2a; }
.keynote span { flex: 1 1 100%; }
.keynote button { font-size: 11px; padding: 2px 8px; }
.keynote .keyswap { border-color: #e8a04a; color: #ffd9a0; }
/* keys straight to an ability, and rebinding a bar slot where it sits (owner 2026-09-29) */
.keydrop { color: #d9897a; }
.abkeyadd { margin: 2px 0 6px; font-size: 11.5px; }
.abkeyadd > button { font-size: 11.5px; padding: 3px 10px; }
.abkeypick { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; max-height: 220px; overflow: auto; }
.abkeyrow { display: flex; gap: 10px; align-items: baseline; text-align: left; padding: 3px 8px; font-size: 11.5px; }
.abkeyrow b { flex: 1; }
.abkeyrow span { color: #9d98ab; }
.abkeyrow .meta { min-width: 42px; text-align: right; }
.keyprompt { position: fixed; z-index: 60; max-width: 340px; padding: 8px 10px; font-size: 12px; color: #e8dcc8;
  background: #17131bf2; border: 1px solid #e8a04a; box-shadow: 0 6px 18px #000a; }
.keyprompt .kphead { color: #ffe08a; font-weight: 600; margin-bottom: 2px; }
.keyprompt .muted { font-size: 11px; }
.keyprompt > button { margin-top: 6px; font-size: 11px; padding: 2px 8px; }
.keyprompt .keynote { margin: 6px 0 0; }
/* ============ TWO MORE BARS (owner 2026-09-29) ============================
   "add an extra hotbar below the main hotbar, and then a hotbar down to the
   right with 12 slots". Bar 2 is a second row in the same column as the
   main bar (#barstack), so it lines up slot for slot; everything that sat
   above the bar row - the F-bar, the heal shelf, the chat, the channel bars -
   is lifted by --xrow, one row and its gap, so nothing lands on anything. */
#hud { --xrow: 51px; }
#barstack { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
#hotbar2 { display: flex; gap: 5px; min-width: 0; }
/* the portal stays beside the "1" of the main bar, not the second row */
#barrow > #btn-recall { align-self: flex-start; }
/* DRAG AN ABILITY ONTO ANOTHER KEY, AND THE LOCK (owner 2026-10-06): the
   padlock at the bars' far end, small; the slot being dragged dims, the one
   under it lights gold */
#barrow > #barlock { align-self: flex-start; flex: none; width: 22px; height: 22px; padding: 0; margin: 0;
  font-size: 12px; line-height: 20px; text-align: center; background: #1b1a24cc; border: 1px solid #3a3748;
  border-radius: 5px; cursor: pointer; opacity: .7; filter: grayscale(.4); }
#barrow > #barlock:hover { opacity: 1; filter: none; border-color: #e8a04a88; }
#barrow > #barlock.on { opacity: 1; filter: none; border-color: #e8a04a; background: #2a2116e6; }
.hslot.dragging { opacity: .45; }
.hslot.droptarget { box-shadow: 0 0 0 2px #ffd873, 0 0 10px #ffd87388; }
/* BAR DROPS (ui.js, owner 2026-10-11 "adding items to the hotbar is wonky"):
   the F-key that WILL take the drag is lit gold - it had the class and no
   look at all - and one that will refuse it red, so a refusal is never a
   mystery; and the thing being dragged rides under the cursor (#dragghost) */
.wslot.dragging { opacity: .45; }
.wslot.droptarget { border-color: #ffd873; background: #2a2414d8; box-shadow: 0 0 0 2px #ffd873, 0 0 10px #ffd87388; }
.hslot.droptarget.dropno, .wslot.droptarget.dropno { border-color: #e0453f; background: #2a1414d8; box-shadow: 0 0 0 2px #e0453f, 0 0 10px #e0453f66; }
#dragghost { position: fixed; left: 0; top: 0; z-index: 2147483000; pointer-events: none; will-change: transform;
  width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; box-sizing: border-box;
  background: #16151dd0; border: 1px solid #ffd87399; border-radius: 5px; box-shadow: 0 3px 10px #000a; }
#dragghost.hidden { display: none; }
#dragghost.away { visibility: hidden; }
#dragghost img { width: 30px; height: 30px; image-rendering: pixelated; }
#dragghost .gq { position: absolute; right: 2px; bottom: 0; font-size: 10px; font-weight: 600; color: #e8d8a0; text-shadow: 0 1px 2px #000; }
#dragghost.ab { width: auto; min-width: 56px; height: 30px; padding: 0 8px; font-size: 11px; white-space: nowrap; }
#dragghost.off { opacity: .6; border-color: #e0453f; box-shadow: 0 0 0 1px #e0453f88, 0 3px 10px #000a; }
/* the corner bar: 3 rows of 4 smaller slots, right of the F-bar and the
   heal shelf, left of the side buttons, above the mana glass, below the
   battle list - checked at 1280x720, 1584x805 and 1920x1080 */
#hotbar3 { position: absolute; right: 74px; bottom: calc(66px + var(--xrow, 51px));
  display: grid; grid-template-columns: repeat(4, 56px); gap: 4px; }
#hotbar3 .hslot { width: 56px; height: 40px; flex: none; }
#hotbar3 .hslot .ab { font-size: 10px; line-height: 1.1; padding: 0 2px; }
#hotbar3 .hslot .num, #hotbar2 .hslot .num { font-size: 9px; }
.xbar .hslot.unusable .ab { color: #6a6780 !important; }
.xbar .hslot.unusable { opacity: .6; }
/* ============ ALWAYS HEALS <NAME> ======================================
   Owner, 2026-09-30: "Make it so that I can decide who I will heal with
   mending light, so when I press that hotkey, it always heal that person".
   A slot pinned to somebody says who, bottom right in the heal's green, and
   is drawn dim while the screen cannot see them in reach (a hint - the
   server decides). See ALWAYS HEALS <NAME> - ON THE BAR in ui.js. */
.hslot .pin { position: absolute; right: 3px; bottom: 1px; z-index: 2; font-size: 9px; line-height: 1;
  color: #7fd08a; text-shadow: 0 1px 2px #000; pointer-events: none; max-width: 90%; overflow: hidden; white-space: nowrap; }
.hslot.pinned:not(.sel) { border-color: #3f6a48; }
.hslot.pinned .ab { line-height: 1.15; }
.hslot.pinfar { opacity: .45; }
.hslot.pinfar .pin { color: #8a9a8e; }
#hotbar3 .hslot .pin { font-size: 8px; right: 2px; }
.itemmenu.healpins button.on { color: #7fd08a; }
.itemmenu.healpins .pinwhy { font-size: 10px; color: #6a6780; margin-left: 6px; }
.itemmenu.healpins .pintype { display: flex; gap: 4px; padding: 3px 6px; }
.itemmenu.healpins .pintype input { width: 118px; font-size: 12px; }
.itemmenu.healpins .pintype button { padding: 3px 8px; border: 1px solid #46425a; }
.keypin { color: #7fd08a; margin-left: 4px; }
.keypin.on { border-color: #3f6a48; }

/* ------------------------------------------------ 1: the loot window */
/* A proper little case of slots, in the same materials as the rest of the
   interface: the bag's own slot look, a draggable header, and a close button.
   4: it can be dragged anywhere, so it never has to sit on top of the quests. */
#lootwin {
  position: absolute; left: 12px; top: 120px; width: 226px;
  background: #14131bee; border: 1px solid #34313f; border-radius: 7px;
  padding: 8px; box-shadow: 0 12px 40px #000a;
}
#lootwin .lhead {
  display: flex; align-items: center; gap: 6px; margin: -8px -8px 8px; padding: 7px 9px;
  font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: #e8a04a;
  background: #1b1a24; border-bottom: 1px solid #2e2b3a; border-radius: 7px 7px 0 0;
  cursor: move; user-select: none;
}
#lootwin .lhead .muted { text-transform: none; letter-spacing: 0; font-size: 10px; flex: 1; }
#lootwin .lhead .close { margin-left: auto; padding: 1px 7px; font-size: 12px; cursor: pointer; }
/* THE PINNED RECIPES. Same furniture as the loot window - it is the same kind
   of thing, a small list you keep an eye on while you are doing something
   else - so it inherits the header, the drag handle and the close button
   rather than inventing a second look for the same idea. */
#pinwin {
  /* ABOVE THE PANELS. Windows are given inline z-indexes in a band from 20 up
     as they are clicked; this had none at all, so the crafting panel you pin
     FROM covered the thing you just pinned. It sits above that whole band and
     still well under the menus and tooltips at 1000. */
  z-index: 500;
  position: absolute; right: 12px; top: 120px; width: 214px;
  background: #14131bee; border: 1px solid #34313f; border-radius: 7px;
  padding: 8px; box-shadow: 0 12px 40px #000a;
}
#pinwin .lhead {
  display: flex; align-items: center; gap: 6px; margin: -8px -8px 8px; padding: 7px 9px;
  font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: #e8a04a;
  background: #1b1a24; border-bottom: 1px solid #2e2b3a; border-radius: 7px 7px 0 0;
  cursor: move; user-select: none;
}
#pinwin .lhead .close { margin-left: auto; padding: 1px 7px; font-size: 12px; cursor: pointer; }
.pinrow { display: flex; gap: 7px; padding: 6px 0; border-top: 1px solid #262433; }
.pinrow:first-child { border-top: 0; }
.pinrow img { width: 30px; height: 30px; image-rendering: pixelated; flex: 0 0 30px; }
.pinbody { flex: 1; min-width: 0; }
.pinname { font-size: 11.5px; font-weight: 600; color: #f0e4cc; display: flex; gap: 4px; }
.pinname .x { margin-left: auto; cursor: pointer; color: #6d675d; font-size: 11px; padding: 0 3px; }
.pinname .x:hover { color: #e0606a; }
.pinmat { font-size: 10.5px; color: #8b8375; display: flex; justify-content: space-between; gap: 6px; }
.pinmat b { font-weight: 500; }
.pinmat.ok b, .pinmat.ok { color: #7fd08a; }
.pinmat.no b, .pinmat.no { color: #d08a6a; }
.pindone { font-size: 10px; color: #7fd08a; letter-spacing: .08em; text-transform: uppercase; }
/* the Craft button on a pinned recipe that is ready to make, here (2026-10-09) */
.pincraft { margin-top: 4px; align-self: flex-start; font-size: 11px; padding: 2px 12px; cursor: pointer;
  background: #2f5a34; color: #e8f5e0; border: 1px solid #5fae6a; border-radius: 3px; }
.pincraft:hover { background: #3d7444; }
/* THE PIN READS WHITE. It sits on a row that is often dimmed and saying no,
   so a soft grey label disappeared into it - and this is the one control on
   that row that still works when everything else is refused. */
.craftrow .pin {
  padding: 2px 7px; font-size: 11px; align-self: center; background: #23202e;
  border: 1px solid #4a4658; color: #ffffff; font-weight: 600;
  cursor: pointer; border-radius: 4px;
}
.craftrow .pin:hover { background: #2e2a3c; border-color: #6a6478; }
.craftrow .pin.on { color: #fff8e0; border-color: #8a7434; background: #33291a; }
#lootlist {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 3px;
  max-height: calc(46vh / var(--uiz, 1)); overflow-y: auto;
}
#lootlist .slot.empty { background: #0c0b10; border-style: dashed; border-color: #241f2e; cursor: default; }
#lootlist .slot.empty:hover { border-color: #241f2e; }
#lootlist .slot.relic { box-shadow: 0 0 0 1px #e8a04a inset, 0 0 10px #e8a04a44; }

/* ------------------------------ a barrel, an urn, a chest: the box to loot */
/* Owner, 2026-10-06 - client/js/lootbox.js. The loot window's furniture
   (the same header, drag handle and close button) with a list of rows:
   picture, name in its rarity's colour, how many - and Loot all. */
#lootbox {
  position: fixed; left: 55%; top: 30%; width: 232px; z-index: 60;
  background: #14131bee; border: 1px solid #34313f; border-radius: 7px;
  padding: 8px; box-shadow: 0 12px 40px #000a;
}
#lootbox .lhead {
  display: flex; align-items: center; gap: 6px; margin: -8px -8px 8px; padding: 7px 9px;
  font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: #e8a04a;
  background: #1b1a24; border-bottom: 1px solid #2e2b3a; border-radius: 7px 7px 0 0;
  cursor: move; user-select: none;
}
#lootbox .lhead .lbtitle { flex: 1; }
#lootbox .lhead .close { margin-left: auto; padding: 1px 7px; font-size: 12px; cursor: pointer; }
#lootbox .lbrows { display: flex; flex-direction: column; gap: 3px; max-height: calc(44vh / var(--uiz, 1)); overflow-y: auto; }
#lootbox .lbrow {
  display: flex; align-items: center; gap: 8px; padding: 3px 5px 3px 3px; cursor: pointer;
  border: 1px solid #262433; border-radius: 4px; background: #191822;
}
#lootbox .lbrow:hover { border-color: #e8a04a88; background: #211f2c; }
#lootbox .lbrow .slot { width: 30px; height: 30px; flex: 0 0 30px; cursor: pointer; }
#lootbox .lbrow .slot.relic { box-shadow: 0 0 0 1px #e8a04a inset, 0 0 10px #e8a04a44; }
#lootbox .lbname { flex: 1; min-width: 0; font-size: 12px; color: #f0e4cc; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#lootbox .lbqty { font-size: 11px; color: #c8b890; white-space: nowrap; }
#lootbox .lbempty { padding: 14px 0 12px; text-align: center; color: #8b8375; font-style: italic; font-size: 13px; }
#lootbox .lbfoot { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
#lootbox .lball { flex: 1; padding: 5px 0; font-weight: 600; }
#lootbox .lball:disabled { opacity: .45; cursor: default; }
#lootbox .lbkey { font-size: 10px; }

/* --------------------------------------------- 2: the GLOBAL DROP splash */
#globalwin {
  position: absolute; left: 50%; top: 34%; transform: translate(-50%, -50%);
  min-width: 300px; text-align: center; padding: 18px 26px 16px;
  background: linear-gradient(180deg, #241a08f2, #16110af2);
  border: 2px solid #e8a04a; border-radius: 10px;
  box-shadow: 0 0 60px #e8a04a55, 0 18px 60px #000c;
  z-index: 40;
}
#globalwin .gtitle {
  font-size: 24px; font-weight: 800; letter-spacing: 6px; color: #ffd873;
  text-shadow: 0 0 18px #e8a04a; margin-bottom: 12px;
  animation: globalpulse 1.2s ease-in-out infinite alternate;
}
@keyframes globalpulse { from { text-shadow: 0 0 8px #e8a04a66; } to { text-shadow: 0 0 26px #ffd873; } }
#globalwin .grow { display: flex; flex-direction: column; align-items: center; gap: 4px; margin-bottom: 8px; }
#globalwin .grow img { width: 48px; height: 48px; image-rendering: pixelated; filter: drop-shadow(0 0 8px #e8a04a); }
#globalwin .grow b { color: #ffe9b0; font-size: 15px; }
#globalwin .grow .gd { color: #b8a67a; font-size: 11px; font-style: italic; max-width: 260px; }
#globalwin .gm { color: #8a7a5c; font-size: 11px; margin: 4px 0 10px; }
#globalwin button { background: #3a2c14; border-color: #6a5424; }

/* ------------------------------------------------- 3: the Hall of Glory */
.gloryrow { border: 1px solid #2b2937; border-radius: 6px; padding: 7px 9px; margin-bottom: 6px; background: #17161f; }
.gloryrow.big { border-color: #e8a04a66; background: #1d1710; }
.gloryrow .gh { display: flex; align-items: center; gap: 8px; font-size: 12px; margin-bottom: 4px; }
.gloryrow .badge { font-size: 9px; letter-spacing: 1px; padding: 1px 6px; border-radius: 3px;
  background: #2b3a2b; color: #a8e8b0; }
.gloryrow .badge.hot { background: #3a2708; color: #ffd873; box-shadow: 0 0 8px #e8a04a44; }
.gloryrow .gi { display: flex; gap: 6px; flex-wrap: wrap; }
.gloryrow .gc { position: relative; width: 26px; height: 26px; background: #101018; border-radius: 3px;
  display: flex; align-items: center; justify-content: center; }
.gloryrow .gc img { width: 20px; height: 20px; image-rendering: pixelated; }
.gloryrow .gc i { position: absolute; right: 0; bottom: -2px; font-size: 9px; font-style: normal; color: #e8d8a0; }

/* ----------------------------------------------- 5: the founding stone */
#signpop, #signnote {
  position: absolute; left: 50%; top: 90px; transform: translateX(-50%);
  width: 380px; padding: 16px 20px; z-index: 30; text-align: center;
  background: linear-gradient(180deg, #201c14f4, #171410f4);
  border: 1px solid #8a7a5c; border-radius: 8px;
  box-shadow: 0 14px 50px #000c;
}
#signpop h2, #signnote h2 { margin: 0 0 8px; font-size: 16px; letter-spacing: 2px; color: #e8d8a0; }
/* clear of #signpop entirely. 42px did not: the sign card is ~105px tall
   (16 padding + a 16px heading with its 8 margin + two lines of 12.5/1.65
   + 16 padding), so a note offset by 42 landed square on top of a sign or a
   gravestone. 210 puts it below the tallest of them. */
#signnote { top: 210px; }
#signpop p, #signnote p { margin: 0; font-size: 12.5px; line-height: 1.65; color: #c8bda0; font-style: italic; }

/* 6: the Server 1 address on the host screen */
#onlineinfo { font-size: 12px; margin-top: 4px; }
#onlineinfo b { color: #7fd08a; }
#onlineinfo .addr { color: #ffe08a; font-family: Consolas, monospace; user-select: text; margin-right: 6px; }

/* 7: dragging something out of the bag to throw it on the ground */
body.dragitem #game { cursor: grabbing; }

/* 3: our own confirmation window, so a permanent choice never comes from a
   Windows box that knows nothing about the game */
#confirm { width: 420px; z-index: 900; }
#confirm p { color: #c9c6d6; line-height: 1.6; }
#confirm .row { display: flex; gap: 8px; margin-top: 14px; }
#confirm button.danger { flex: 1; background: #7a2a1a; border-color: #a8452a; padding: 9px; }
#confirm button.danger:hover { background: #94341f; }
#confirm button.ghost { background: transparent; border-style: dashed; }
/* 35: naming a place on the map, asked in the game rather than by the system */
#confirm input {
  width: 100%; margin-top: 12px; padding: 9px 10px;
  background: #16131c; color: #efe9dd; font: inherit;
  border: 1px solid #3a3346; border-radius: 3px;
}
#confirm input:focus { outline: none; border-color: #a8894a; }
#confirm .row button { flex: 1; padding: 9px; }
#confirm label.check {
  display: grid; grid-template-columns: auto 1fr; align-items: center;
  gap: 4px 8px; margin-top: 12px; cursor: pointer;
}
#confirm label.check input { width: auto; margin: 0; accent-color: #a8894a; }
#confirm label.check span { color: #efe9dd; }
#confirm label.check small { grid-column: 2; font-size: 12px; }

/* 3: the character sheet */
.csheet-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
.csheet-head b { font-size: 16px; color: #e8e6ef; }
.csheet-head span { font-size: 12px; color: #8b8898; }
.csheet-head .voc { margin-left: auto; font-size: 13px; }
.csheet { display: flex; flex-direction: column; gap: 2px; margin-bottom: 4px; }
.csrow { display: grid; grid-template-columns: 110px 96px 1fr; gap: 8px; align-items: baseline;
  padding: 3px 6px; border-radius: 3px; font-size: 12px; }
.csrow:nth-child(odd) { background: #17161f; }
.csrow .ck { color: #8b8898; }
.csrow .cv { color: #e8e6ef; font-weight: 600; }
.csrow .cn { color: #6a6780; font-size: 11px; }

/* 12: Peder's rentable bag slots */
.depgrid.bagrow { grid-template-columns: repeat(10, 1fr); width: 100%; }
.slot.bagslot .slotname { position: static; font-size: 9px; color: #7d7a8c; text-align: center; }
.slot.bagslot.locked-slot { border-style: dashed; border-color: #3a3448; }
.slot.bagslot.locked-slot:hover { border-color: #c9b46a; }
.slot.bagslot.locked-slot .slotname { color: #c9b46a; }
/* a rented slot lights up when a storage bag is dragged over it */
.slot.bagslot.droptarget { border-color: #7fd08a; box-shadow: 0 0 8px #7fd08a66; background: #1a241bd0; }
/* three weapons stay ready - the one in your hands wears the ring */
.slot.eq.inhand { border-color: #e8a04a; box-shadow: 0 0 9px #e8702c66; background: #241d16d0; }
.slot.eq.inhand .slotname { color: #e8a04a; }

/* 6: F1/F2/F3 wear the weapon kind they are reserved for */
.wslot .wcat { position: absolute; right: 2px; top: 0; font-size: 9px; font-weight: 700; opacity: .75; }
.wslot .wcat.melee { color: #e06c4a; }
.wslot .wcat.ranged { color: #7bc86c; }
.wslot .wcat.magic { color: #6aa9e0; }

/* --------------------------------------------------- picking a server */
.serverhead { display: flex; justify-content: space-between; align-items: center;
  color: #7d7a8c; font-size: 12px; letter-spacing: 1px; text-transform: uppercase; margin-bottom: 2px; }
.serverhead button { text-transform: none; letter-spacing: 0; }
#btn-refresh:disabled { opacity: .5; }
#serverlist { display: flex; flex-direction: column; gap: 6px; }
#serverlist.dim { opacity: .4; }
.servercard {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; cursor: pointer;
  padding: 9px 12px; background: #17161f; border: 1px solid #2b2937; border-radius: 6px;
}
.servercard:hover { border-color: #55506a; }
.servercard.on { border-color: #e8a04a; background: #201c19; }
.servercard b { font-size: 15px; color: #e8e6ef; }
.servercard .sblurb { grid-column: 1; color: #8b8898; font-size: 12px; }
.servercard .sright { grid-row: 1 / span 2; align-self: center; text-align: right; display: flex; flex-direction: column; gap: 3px; align-items: flex-end; }
.servercard .shost { color: #6a6780; font-size: 11px; }
/* is it up? green and it is, red and it is not */
.sdot { font-size: 10px; letter-spacing: 1px; text-transform: uppercase;
  padding: 2px 8px 2px 18px; border-radius: 10px; position: relative; }
.sdot::before { content: ''; position: absolute; left: 7px; top: 50%; margin-top: -3px;
  width: 6px; height: 6px; border-radius: 50%; }
.sdot.online  { color: #7fd08a; background: #14231a; }
.sdot.online::before  { background: #5ec86a; box-shadow: 0 0 6px #5ec86a; }
.sdot.offline { color: #e08a8a; background: #241416; }
.sdot.offline::before { background: #d0455a; }
.sdot.checking { color: #8b8898; background: #1a1922; }
.sdot.checking::before { background: #6a6780; animation: blink 1s ease-in-out infinite; }
@keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
.servercard.on .shost { color: #a8894a; }
#btn-otherserver { margin-top: 2px; }

/* -------------------------------------------------- the first welcome */
#welcome { width: 560px; z-index: 120; }
#welcome h2 { color: #e8a04a; }
#welcomebody { max-height: calc(56vh / var(--uiz, 1)); overflow: auto; }
#welcomebody .wsec { margin: 0 0 14px; }
#welcomebody .wsec h4 { margin: 0 0 4px; color: #ffe08a; font-size: 13px; letter-spacing: .5px; }
#welcomebody .wsec p { margin: 0; color: #c9c6d6; font-size: 12.5px; line-height: 1.6; }
#welcomebody .wsec p b { color: #e8e6ef; }
#welcome .row { display: flex; gap: 8px; margin-top: 6px; }
#welcome .row button { flex: 1; padding: 10px; }
#welcome .row button.ghost { flex: 0 0 auto; background: transparent; border-style: dashed; }

/* a bag opened out of your inventory */
.panel[data-name="bag"] { width: 420px; }
.panel[data-name="bag"] .depgrid { grid-template-columns: repeat(10, 1fr); }
#bagcap { margin: 6px 0 2px; }

/* what you are carrying right now - one pill per buff, counting down */
#buffpills { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.buffpill {
  display: flex; align-items: center; gap: 5px;
  background: #1b1a24d0; border: 1px solid #6a5a2a; border-radius: 3px;
  padding: 2px 6px; font-size: 11px; color: #ffe9b0;
}
.buffpill b { color: #fff; font-weight: 600; }
.buffpill .secs { color: #a5a2b4; font-variant-numeric: tabular-nums; }
.buffpill.fading { border-color: #4a4230; opacity: .65; }
.buffpill.weather { border-color: #4a6a8a; color: #c8dcf0; }
/* and the red one for a sky that is actually taking something off you */
.buffpill.weather.bad { border-color: #a8452f; color: #ffc0a8; background: #241618d0; }
.buffpill.weather.bad b { color: #ff9a7a; }
/* SET PILLS (ui.js): the set's own colour on the rim, set by the code; the
   text a warm gold, and the help cursor so the hover reads as "there is
   more here" - the whole text of every tier is on it */
.buffpill.set, .buffpill.aura { color: #f0dca0; cursor: help; }
.buffpill.aura b { color: #fff3c8; }

/* ------------------------------------------------------- picking a lock */
/* Deliberately in the middle of the screen and deliberately loud - it is the
   only thing you should be looking at while you are doing it. */
.lockwrap {
  position: absolute; inset: 0; z-index: 70; display: flex;
  align-items: center; justify-content: center; background: #0a0910a0;
}
.lockbox {
  background: #16151d; border: 1px solid #6a5a2a; border-radius: 8px;
  padding: 18px 22px; width: 440px; box-shadow: 0 18px 50px #000c;
}
.lockbox h3 { margin: 0 0 10px; color: #e8c14a; font-size: 16px; }
.lockpins { display: flex; gap: 6px; margin-bottom: 12px; }
.lockpins i {
  flex: 1; height: 6px; background: #26242f; border: 1px solid #3a3748; border-radius: 2px;
}
.lockpins i.set { background: #7fd08a; border-color: #7fd08a; }
.lockpins i.now { border-color: #e8c14a; }
.locktrack {
  position: relative; height: 30px; background: #0c0b10;
  border: 1px solid #3a3748; border-radius: 3px; overflow: hidden;
}
.lockband { position: absolute; top: 0; bottom: 0; background: #2e5a34; border-left: 1px solid #7fd08a; border-right: 1px solid #7fd08a; }
.lockmark { position: absolute; top: 0; bottom: 0; width: 3px; background: #e8c14a; box-shadow: 0 0 8px #e8c14a; }
.lockbox p { margin: 10px 0; font-size: 12px; }
.lockbox.bad .locktrack { border-color: #d0455a; }
/* CARRY CAPACITY, and it answers a yes/no question at a glance: green means
   there is room, red means there is not. The old grey said "here is a
   number" and made you read both halves of it and do the sum yourself. */
#loadbar.load { background: #12111ac0; border: 1px solid #3a5a3c; color: #7ed08a; }
#loadbar.full { border-color: #d0455a; color: #ff9aa8; }
/* the same line in the bag, where you are when you decide what to drop */
.carrycap {
  margin-left: auto; font-size: 11px; color: #7ed08a; white-space: nowrap;
  font-variant-numeric: tabular-nums; align-self: center;
}
.carrycap.full { color: #ff9aa8; }

/* how worn a piece is - green while it is fine, red once it is costing you */
.durbar { position: absolute; left: 3px; right: 3px; bottom: 2px; height: 3px; background: #0c0b10; border-radius: 1px; overflow: hidden; }
.durbar i { display: block; height: 100%; background: #5ec86a; }
.durbar.bad i { background: #d0455a; }
.slot.eq { position: relative; }

/* the five bag slots on your person, between your gear and your bag. A bag in
   one of these makes the grid below it bigger - it does not open a window. */
.bagbelt { display: flex; align-items: center; gap: 6px; margin: 8px 0 0; }
.bagbelt .beltlabel { font-size: 9px; letter-spacing: 1px; text-transform: uppercase;
  color: #7d7a8c; width: 30px; line-height: 1.1; }
#invbags { display: grid; grid-template-columns: repeat(5, 1fr); gap: 4px; flex: 1; }
#invbags .slot { min-height: 34px; }

/* the level of the thing you are about to swing at, coloured by what it pays:
   green is worth your time, grey is not */
.battlerow .blv { grid-column: 1; min-width: 20px; text-align: center; font-size: 10px;
  padding: 1px 3px; border-radius: 2px; background: #241f30; color: #b9b4c8;
  font-variant-numeric: tabular-nums; }
/* ============ RED, YELLOW, GREEN ==============================================
   Owner, 2026-09-28: "if the monsters is much higher, it's in red, same level
   yellow, low level, green." A creature's level against yours, everywhere it
   is written beside its name - over its head (render.js), the target frame,
   the hover tip and this list. The three colours are LEVEL_TONE_COLOR in
   shared/config.js and the bands are levelTone() beside it. The list's badge
   was tinted by xp worth before (good / fair / poor); that is in its tooltip. */
.lvtone { font-weight: 700; font-variant-numeric: tabular-nums; }
.lvtone.high, #targetframe .tlv.high { color: #ff5a48; }
.lvtone.even, #targetframe .tlv.even { color: #ffd23f; }
.lvtone.low, #targetframe .tlv.low { color: #74e06a; }
#worldtip .lvtone { margin-left: 8px; }
/* red on a red health bar needs a harder edge than the soft glow the row
   gives it - a tight black ring, still no chip behind it (see .tlv above) */
#targetframe .tlv.lvtone { text-shadow: 0 0 2px #000, 0 0 2px #000, 0 1px 3px #000; }
.battlerow .blv.high { background: #46201b; color: #ff7a68; }
.battlerow .blv.even { background: #3d3418; color: #ffd23f; }
.battlerow .blv.low { background: #203c1d; color: #8ee682; }

/* the titles panel: what you have done, and the name you get to wear for it */
#titlelist .titlerow {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 8px;
  align-items: center; padding: 6px 8px; margin: 3px 0;
  border: 1px solid #24222e; border-radius: 3px; background: #14121ca0;
}
#titlelist .titlerow b { color: #c9b46a; font-size: 13px; }
#titlelist .titlerow span { color: #8b8898; font-size: 11px; grid-column: 1 / -1; }
#titlelist .titlerow .tprog { grid-column: 2; grid-row: 3; text-align: right; color: #7d7a8c; }
#titlelist .titlerow .tbar {
  grid-column: 1; grid-row: 3; height: 4px; background: #201d29;
  border: 1px solid #2c2836; border-radius: 2px; overflow: hidden;
}
#titlelist .titlerow .tbar i { display: block; height: 100%; background: #6a5a2a; }
#titlelist .titlerow.earned { cursor: pointer; border-color: #4a4230; }
#titlelist .titlerow.earned:hover { border-color: #c9b46a; background: #1d1a24; }
#titlelist .titlerow.earned .tbar i { background: #c9b46a; }
#titlelist .titlerow.locked b { color: #6d6a7a; }
#titlelist .titlerow.on { border-color: #e8a04a; background: #241d14; }
/* ASCII ONLY IN content:. A tick here came out of the packager mangled -
   the asar step mangles anything non-ASCII, and it read as a stray
   apostrophe and a 3 in front of the word. Plain letters cannot be. */
#titlelist .titlerow.on b::after { content: ' - WORN'; color: #e8a04a; font-size: 10px; letter-spacing: 1px; }
/* the standings and the notice board now close from the top, like everything else */
#dialog h2 { position: relative; }
#dialog h2 .close {
  position: absolute; right: 0; top: 0; padding: 0 7px; line-height: 18px;
  background: none; border: 1px solid #2f2c3a; color: #8b8898; border-radius: 2px; cursor: pointer;
}
#dialog h2 .close:hover { color: #e8a04a; border-color: #6a5a2a; }

/* your stable, on the character sheet */
.mountrow { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 2px; }
.mountpick {
  display: flex; align-items: center; gap: 6px; padding: 5px 8px; cursor: pointer;
  border: 1px solid #2b2937; border-radius: 4px; background: #14121ca0; font-size: 12px;
}
.mountpick i { width: 10px; height: 10px; border-radius: 2px; display: block; }
.mountpick b { color: #d8d5e4; font-weight: normal; }
.mountpick span { color: #7d7a8c; font-size: 11px; }
.mountpick:hover { border-color: #6a6480; }
.mountpick.on { border-color: #c9b46a; }
.mountpick.riding { border-color: #e8a04a; background: #241d14; }
.mountpick.riding b::after { content: ' - RIDING'; color: #e8a04a; font-size: 9px; letter-spacing: 1px; }

/* the sheet under the paper doll - smaller, so it does not fill the screen */
.panel[data-name="character"] { width: 400px; }
.panel[data-name="character"] .csheet { gap: 1px; }
.panel[data-name="character"] .csrow { font-size: 10px; padding: 1px 5px; }
.panel[data-name="character"] .csrow .ck { min-width: 78px; }
.panel[data-name="character"] .csrow .cn { font-size: 9px; }
.panel[data-name="character"] h4.sub { margin: 7px 0 3px; font-size: 10px; }
.panel[data-name="character"] .csheet-head { font-size: 12px; }
.panel[data-name="character"] p.muted { font-size: 10px; margin: 3px 0; }
.panel[data-name="character"] .mountpick { padding: 3px 6px; font-size: 11px; }

/* THE PURSE. One number, bottom right of whatever window you are in, and it
   stays there while the rest of the window scrolls under it. It used to be
   written out in two or three places inside a shop and the copy you wanted
   was always the one that had scrolled off the top. */
.purse {
  position: sticky; bottom: -18px; z-index: 2;
  margin: 12px -18px -18px; padding: 9px 18px;
  text-align: right; font-size: 13px; font-weight: bold;
  color: #e8c05a; text-shadow: 0 1px 2px #000;
  background: linear-gradient(#16151d00, #16151de8 42%, #16151d);
  border-radius: 0 0 8px 8px; pointer-events: none;
}
/* Money is this colour and nothing else is. The purse in the corner of a
   window is the same yellow, so a number in it reads as gold anywhere it
   turns up without needing to be labelled. Only the NUMBER - the "g" after
   it is just a unit and stays the colour of the sentence it is in. */
.gold { color: #e8c05a; font-weight: bold; }
/* The x in "2,882x for 250g". A size down and green, so the number in front
   of it reads as a COUNT at a glance and cannot be mistaken for the money
   further along the same line. */
.times { font-size: 0.82em; color: #7fd08a; font-weight: bold; }

/* ===================================================== THE RUNE BENCH =====
   Drawn the way the game is drawn: hard edges, no blur, no soft gradients and
   no true circles. Every shape steps in whole pixels - the slab is a stepped
   stone block with a lit top edge and a dark bottom one, the sockets are cut
   octagons with a bevel, and all the shading is dithering rather than a fade.
   The pixel unit is 4px so it reads as art at any window size. */
#runetable {
  position: relative; height: 200px; margin: 8px 0 12px;
  image-rendering: pixelated;
  background-color: #3a3244;
  /* the slab: hard-stop dither, no gradient anywhere */
  background-image:
    repeating-linear-gradient(0deg, #00000018 0 4px, #0000 4px 8px),
    repeating-linear-gradient(90deg, #ffffff08 0 4px, #0000 4px 12px);
  /* a stepped stone edge, built from flat shadows instead of a border */
  box-shadow:
    inset 0 4px 0 #5a4e70, inset 4px 0 0 #4a4058,
    inset -4px 0 0 #241e2c, inset 0 -4px 0 #1a1622,
    0 0 0 4px #241e2c, 0 4px 0 4px #12101a;
  overflow: hidden;
}
/* chipped corners, so the slab is not a rectangle with rounded lies on it */
#runetable::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(135deg, #12101a 0 8px, #0000 8px) top left / 12px 12px no-repeat,
    linear-gradient(225deg, #12101a 0 8px, #0000 8px) top right / 12px 12px no-repeat,
    linear-gradient(45deg,  #12101a 0 8px, #0000 8px) bottom left / 12px 12px no-repeat,
    linear-gradient(315deg, #12101a 0 8px, #0000 8px) bottom right / 12px 12px no-repeat;
}
#runetable .rsockets {
  position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; gap: 24px;
}
/* a socket is an octagon cut into the stone - eight straight edges, no curve */
.rsock {
  width: 56px; height: 56px; cursor: pointer;
  image-rendering: pixelated;
  background-color: #0d0b12;
  background-image: repeating-linear-gradient(0deg, #ffffff08 0 4px, #0000 4px 8px);
  clip-path: polygon(
    16px 0, calc(100% - 16px) 0, 100% 16px, 100% calc(100% - 16px),
    calc(100% - 16px) 100%, 16px 100%, 0 calc(100% - 16px), 0 16px);
  /* the bevel: light where the cut catches, dark where it falls away */
  box-shadow: inset 0 4px 0 #241e2c, inset 0 -4px 0 #5a4a70;
  display: flex; align-items: center; justify-content: center;
  outline: 4px solid #5a4a70; outline-offset: -4px;
}
.rsock:hover { outline-color: #8a6ad0; }
.rsock.on { outline-color: #b89aec; background-color: #1a1230; }
.rsock img {
  width: 32px; height: 32px; image-rendering: pixelated;
}
/* the tools, as flat blocks laid on the stone */
#runetable .rtools i { position: absolute; display: block; image-rendering: pixelated; }
/* the wheel: an octagon with a square hub, not a ring */
.rt-wheel {
  left: 16px; top: 20px; width: 48px; height: 48px;
  background-color: #6a5a84;
  background-image:
    linear-gradient(#2c2636 0 0) center / 16px 16px no-repeat,
    repeating-linear-gradient(0deg, #00000022 0 4px, #0000 4px 8px);
  clip-path: polygon(
    14px 0, calc(100% - 14px) 0, 100% 14px, 100% calc(100% - 14px),
    calc(100% - 14px) 100%, 14px 100%, 0 calc(100% - 14px), 0 14px);
  box-shadow: 4px 4px 0 #1a1622;
}
/* the chisel: a stepped blade, drawn in two flat blocks */
.rt-chisel {
  right: 24px; top: 20px; width: 12px; height: 44px;
  background:
    linear-gradient(#c8c4d4 0 0) top / 12px 24px no-repeat,
    linear-gradient(#6a5a44 0 0) bottom / 12px 20px no-repeat;
  box-shadow: 4px 4px 0 #1a1622, inset -4px 0 0 #00000033;
}
/* the loupe: a square lens in a square frame */
.rt-loupe {
  right: 24px; bottom: 20px; width: 28px; height: 28px;
  background-color: #8a6ad0;
  box-shadow: inset 0 0 0 4px #6a5a44, 4px 4px 0 #1a1622;
}
/* READY. Not a glow - a hard violet dither over the stone, the way a pixel
   game says "this is lit" without a blur filter anywhere near it. */
.rglow {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background-image:
    repeating-linear-gradient(45deg, #b89aec40 0 4px, #0000 4px 8px),
    repeating-linear-gradient(-45deg, #8a6ad030 0 4px, #0000 4px 8px);
}
#runetable.ready .rglow { opacity: 1; }
#runetable.ready .rsock { outline-color: #b89aec; background-color: #241a44; }
#runetable.ready .rt-wheel { background-color: #8a6ad0; }
/* the stones in the tray sit in pixel cells too */
#runebag .dep.on { outline: 4px solid #b89aec; outline-offset: -4px; }

/* ================================================= CRAFTING FEEDBACK =====
   A price is money and reads as money; the bench is visibly busy for the beat
   after a craft; and a good run gets a small banner of its own rather than one
   more line of chat. */
.ci .coin, .pinmat b.coin { color: #e8c05a; }
.ci em.coin.no { color: #d0705a; }
.craftlist.busy button, #craftlist.busy button { opacity: .45; pointer-events: none; }
.luckybox {
  position: fixed; left: 50%; top: 20%; transform: translateX(-50%);
  z-index: 1200; pointer-events: none; text-align: center;
  padding: 10px 18px; border-radius: 4px;
  background: #16151df2; border: 1px solid #e8c05a;
  box-shadow: 0 8px 30px #000b, 0 0 20px #e8c05a33;
  display: flex; flex-direction: column; gap: 2px;
  animation: luckypop 1.9s ease-out forwards;
}
.luckybox b { color: #e8c05a; font-size: 15px; letter-spacing: .04em; }
.luckybox span { color: #ded9e8; font-size: 12.5px; }
.luckybox.hidden { display: none; }
@keyframes luckypop {
  0%   { opacity: 0; transform: translateX(-50%) translateY(8px) scale(.94); }
  12%  { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
  80%  { opacity: 1; }
  100% { opacity: 0; transform: translateX(-50%) translateY(-10px); }
}
@media (prefers-reduced-motion: reduce) { .luckybox { animation: none; } }

/* the two sheet options, in the box that never gets rebuilt under them */
#charopts { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; }

/* ---- the tavern wall: the vale's biggest of each fish ----
   Three columns, the weights aligned so they can be compared at a glance,
   and the name given the warm colour because the name is the point. */
.walllist { display: grid; gap: 2px; margin: 4px 0 10px; }
.wallrow {
  display: grid; grid-template-columns: 1fr auto 7.5rem; gap: 10px;
  align-items: baseline; font-size: 12px;
  padding: 3px 0; border-bottom: 1px solid #221f2c;
}
.wallrow .wfish { color: #cfc9dc; }
.wallrow .wkg { color: #9a93ad; font-variant-numeric: tabular-nums; }
.wallrow .wwho { color: #e8d9a8; text-align: right; }
/* an aside, not a greeting - dimmer and italic so it reads as the villager
   mentioning something rather than as a notice pinned to them */
.npcline.missed { color: #9a93ad; font-style: italic; }

/* ============ RUNA'S CUT ==============================================
   Small, centred, and it holds the screen until the reveal has played -
   the button only exists once the stone is out. The name and the stats
   fade up in turn rather than together, which is the difference between a
   reveal and a dialog. */
#gemgate {
  /* same lesson as the password window: a modal is worth nothing if
     something else in the page outranks it */
  position: fixed; inset: 0; z-index: 901;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6,5,9,0.72);
}
#gemgate.hidden { display: none; }
/* ============ CUT IN THE SAME WORKSHOP AS THE CHART ==================
   This was a brown rounded rectangle with a chunky orange button on it -
   fine on its own and nothing at all like the rest of the game once the
   star-chart arrived. It borrows the chart's whole language now: the deep
   blue-black ground, the silver rule with corner marks cut off the square,
   letter-spaced capitals for anything that is a label, and starlight for the
   one thing you are meant to look at. Nothing about the reveal itself moved -
   the canvas, the timings and the held screen are untouched. */
.gembox {
  width: min(320px, calc(90vw / var(--uiz, 1)));
  background: #0a0e1c radial-gradient(ellipse 90% 70% at 50% 38%, #101a33 0%, #0a0e1c 62%, #05070f 100%);
  border: 1px solid rgba(200, 212, 232, .28);
  border-radius: 2px;
  padding: 18px 18px 16px; text-align: center;
  box-shadow: 0 0 0 1px #04060d, 0 24px 64px rgba(0, 0, 0, .78),
              inset 0 1px 0 rgba(200, 212, 232, .10);
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  position: relative;
}
/* the astrolabe corner marks, drawn as two rules rather than an image */
.gembox::before, .gembox::after {
  content: ''; position: absolute; width: 13px; height: 13px; pointer-events: none;
  border: 1px solid rgba(200, 212, 232, .42);
}
.gembox::before { left: 6px; top: 6px; border-right: 0; border-bottom: 0; }
.gembox::after { right: 6px; bottom: 6px; border-left: 0; border-top: 0; }
#gemcanvas { image-rendering: pixelated; width: 220px; height: 220px; }
#gemname {
  font: 600 15px/1.2 ui-monospace, Consolas, monospace;
  letter-spacing: 3px; text-transform: uppercase; color: #fff6d8;
  text-shadow: 0 0 10px rgba(255, 246, 216, .35), 0 2px 0 #0009;
  opacity: 0; transform: translateY(5px);
  transition: opacity .28s ease, transform .28s ease;
  min-height: 20px;
}
#gemname.in { opacity: 1; transform: none; }
#gemstats {
  opacity: 0; transform: translateY(5px);
  transition: opacity .3s ease, transform .3s ease;
  font: 12px/1.55 ui-monospace, Consolas, monospace;
  color: #c8d4e8; text-align: left; width: 100%;
  border-top: 1px solid rgba(200, 212, 232, .16); padding-top: 9px;
}
#gemstats.in { opacity: 1; transform: none; }
.gemfirst { margin-top: 7px; color: #e8c46a; letter-spacing: 1px; font-style: normal; }
#btn-gemok {
  margin-top: 4px; padding: 7px 24px;
  font: 600 11px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 3px; text-transform: uppercase;
  background: #131a2e; color: #fff6d8;
  border: 1px solid rgba(200, 212, 232, .38); border-radius: 2px; cursor: pointer;
  transition: background .16s ease, border-color .16s ease;
}
#btn-gemok:hover { background: #1b2440; border-color: #fff6d8; }
#btn-gemok.hidden { display: none; }

/* ============ WHAT THE JOB PAID ======================================
   Owner, 2026-09-26: "they should always give a rewardwindow popup so
   people can FEEL that they got the item. Like the gem opening and
   similar."
   The gem window's own box (.gembox - the star-chart ground, the silver
   rule and its corner marks), a little wider so three tiles sit in a row.
   One level ABOVE the gem gate, because it opens over an NPC's
   conversation that goes on redrawing itself underneath, and a hand-in at
   Runa's counter could in principle land one on top of the other.

   Each tile is a bag slot in the chart's colours: the frame and the name
   take the grade colour when it is gear (the same six bands as the little
   number in the bag) and the thing's own colour when it is not. The slot
   drops in at nearly three times its size with one overshoot - the gem's
   pop - and the words fade up a beat after it, never with it. */
#questgate {
  position: fixed; inset: 0; z-index: 902;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6,5,9,0.72);
}
#questgate.hidden { display: none; }
.qrbox { width: min(380px, calc(92vw / var(--uiz, 1))); cursor: default; }
#qrlabel {
  font: 600 10px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 4px; text-transform: uppercase; color: rgba(200, 212, 232, .62);
}
#qrlabel.main { color: #e8c46a; }
#qrtitle {
  font: 600 15px/1.25 ui-monospace, Consolas, monospace;
  letter-spacing: 3px; text-transform: uppercase; color: #fff6d8;
  text-shadow: 0 0 10px rgba(255, 246, 216, .35), 0 2px 0 #0009;
  max-width: 100%;
}
#qrstage { position: relative; width: 100%; height: 136px; }
#qrcanvas { position: absolute; inset: 0; width: 100%; height: 100%; image-rendering: pixelated; }
/* the slots in one straight row whatever is written under each - a tile
   with a second line under it must not sit higher than its neighbours */
#qrtiles {
  position: absolute; inset: 0; padding-top: 26px;
  display: flex; align-items: flex-start; justify-content: center; gap: 8px;
}
.qrtile {
  --qc: #c8d4e8;
  width: 92px; display: flex; flex-direction: column; align-items: center; gap: 5px;
  visibility: hidden;
}
.qrtile.in { visibility: visible; }
.qrslot {
  position: relative; width: 52px; height: 52px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  background: #0d1326; border-radius: 2px;
  border: 1px solid color-mix(in srgb, var(--qc) 62%, #2b3552);
  box-shadow: 0 0 16px color-mix(in srgb, var(--qc) 34%, transparent),
              inset 0 1px 0 rgba(200, 212, 232, .10);
}
.qrtile.in .qrslot { animation: qrpop .42s cubic-bezier(.2, .8, .3, 1) both; }
@keyframes qrpop {
  0%   { transform: scale(2.8); opacity: 0; }
  18%  { opacity: 1; }
  68%  { transform: scale(.92); }
  100% { transform: scale(1); opacity: 1; }
}
.qrslot img { width: 40px; height: 40px; image-rendering: pixelated; }
.qrn {
  position: absolute; right: 2px; bottom: 1px;
  font: 600 10px/1 ui-monospace, Consolas, monospace; color: #fff6d8;
  text-shadow: 0 1px 0 #000, 0 0 3px #000;
}
.qrlv {
  position: absolute; left: 2px; bottom: 1px;
  font: 600 9px/1 ui-monospace, Consolas, monospace; color: var(--qc);
  text-shadow: 0 1px 0 #000, 0 0 3px #000;
}
.qrname, .qrwhere, .qrpick {
  opacity: 0; transform: translateY(4px);
  transition: opacity .28s ease .16s, transform .28s ease .16s;
  text-align: center; max-width: 92px;
}
.qrtile.in .qrname, .qrtile.in .qrwhere, .qrtile.in .qrpick { opacity: 1; transform: none; }
.qrname {
  font: 600 10px/1.3 ui-monospace, Consolas, monospace;
  letter-spacing: 1px; text-transform: uppercase;
  color: color-mix(in srgb, var(--qc) 72%, #fff6d8);
}
.qrpick { font: 600 8px/1 ui-monospace, Consolas, monospace; letter-spacing: 2px; text-transform: uppercase; color: #e8c46a; }
.qrwhere { font: 10px/1.25 ui-monospace, Consolas, monospace; color: #e8c46a; }
#qrxp, #qrnote {
  opacity: 0; transform: translateY(5px);
  transition: opacity .3s ease, transform .3s ease;
  width: 100%; font: 12px/1.55 ui-monospace, Consolas, monospace; color: #c8d4e8;
}
#qrxp { border-top: 1px solid rgba(200, 212, 232, .16); padding-top: 8px; }
#qrxp:empty { display: none; }
#qrxp.in, #qrnote.in { opacity: 1; transform: none; }
.qrxprow { display: flex; justify-content: space-between; gap: 12px; }
.qrxprow i { font-style: normal; letter-spacing: 2px; text-transform: uppercase; color: rgba(200, 212, 232, .7); font-size: 11px; }
.qrxprow b { font-weight: 600; color: #fff6d8; }
#qrnote { color: #e8c46a; text-align: left; font-size: 11px; line-height: 1.45; }
#qrnote:empty { display: none; }
/* ============ AND THE LEVEL BAR, FILLING ===============================
   Owner, 2026-09-29: "All quests should reward regular experience as well.
   scaling with level. It should feel good." The character experience is
   the biggest line on the card, in the HUD level bar's own gold, with the
   words for how much of a level it is under it - and a copy of the level
   bar that fills by it: what you had is the dim part, what the quest paid
   runs bright over it, and crossing the line flashes the bar and brings up
   "Level N!" (with the level-up sound - see qrApply). */
#qrlevel {
  width: 100%; box-sizing: border-box;
  border-top: 1px solid rgba(200, 212, 232, .16); padding-top: 10px;
  opacity: 0; transform: translateY(5px);
  transition: opacity .3s ease, transform .3s ease;
  font: 12px/1.3 ui-monospace, Consolas, monospace; color: #c8d4e8; text-align: center;
}
#qrlevel.none { display: none; }
#qrlevel.in { opacity: 1; transform: none; }
#qrlvn {
  font: 600 22px/1.1 ui-monospace, Consolas, monospace; letter-spacing: 2px;
  color: #ffe08a; text-shadow: 0 0 12px rgba(240, 185, 84, .55), 0 2px 0 #0009;
}
#qrlvwords {
  margin-top: 4px; font-size: 10px; letter-spacing: 3px; text-transform: uppercase;
  color: rgba(200, 212, 232, .72);
}
#qrlvbar {
  position: relative; height: 9px; margin: 10px 0 5px;
  background: #0c0b10; border: 1px solid rgba(232, 160, 74, .38); border-radius: 3px; overflow: hidden;
}
#qrlvfill, #qrlvgain { position: absolute; top: 0; bottom: 0; left: 0; width: 0; }
#qrlvfill { background: linear-gradient(90deg, #7a4f26, #a8743a); }
#qrlvgain { background: linear-gradient(90deg, #e8a04a, #ffe08a); box-shadow: 0 0 8px rgba(255, 224, 138, .7); }
#qrlvfoot {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 10px; letter-spacing: 2px; text-transform: uppercase; color: rgba(200, 212, 232, .62);
}
/* the number drops in like a tile does, and "Level N!" is the loudest word
   on the card when it comes - it was the size of the footer beside it */
#qrlevel.in #qrlvn { animation: qrlvpop .5s cubic-bezier(.2, 1.5, .4, 1) both; }
@keyframes qrlvpop {
  0%   { opacity: 0; transform: scale(1.8); }
  60%  { opacity: 1; transform: scale(.95); }
  100% { opacity: 1; transform: none; }
}
#qrlvup {
  font-weight: 700; font-size: 16px; letter-spacing: 3px; color: #ffe08a; opacity: 0;
  text-shadow: 0 0 10px rgba(255, 224, 138, .75), 0 2px 0 #0009;
}
#qrlevel.up #qrlvup { animation: qrlvup .6s cubic-bezier(.2, 1.5, .4, 1) both; }
#qrlevel.up #qrlvbar { animation: qrlvflash .7s ease-out; }
@keyframes qrlvup {
  0%   { opacity: 0; transform: scale(1.9); }
  60%  { opacity: 1; transform: scale(.94); }
  100% { opacity: 1; transform: none; }
}
@keyframes qrlvflash {
  0%   { border-color: #fff6d8; box-shadow: 0 0 22px rgba(255, 224, 138, .95); }
  100% { border-color: rgba(232, 160, 74, .38); box-shadow: none; }
}
@media (prefers-reduced-motion: reduce) { #qrlevel.up #qrlvup, #qrlevel.up #qrlvbar, #qrlevel.in #qrlvn { animation: none; opacity: 1; } }
#btn-qrok {
  margin-top: 4px; padding: 7px 24px;
  font: 600 11px/1 ui-monospace, Consolas, monospace;
  letter-spacing: 3px; text-transform: uppercase;
  background: #131a2e; color: #fff6d8;
  border: 1px solid rgba(200, 212, 232, .38); border-radius: 2px; cursor: pointer;
  transition: background .16s ease, border-color .16s ease;
}
#btn-qrok:hover, #btn-qrok:focus-visible { background: #1b2440; border-color: #fff6d8; outline: none; }
#btn-qrok.hidden { display: none; }

/* ============ THE CAT YOU JUST ADOPTED ================================
   Runa's cut, in the stable's colours: the same modal, the same held screen,
   the same button that only exists once the reveal has played. It is a CARD -
   six short lines and out - because a new owner needs to know what the animal
   does, not to read a manual at the counter. */
#catgate {
  position: fixed; inset: 0; z-index: 901;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6,5,9,0.72);
}
#catgate.hidden { display: none; }
.catbox {
  width: min(330px, calc(92vw / var(--uiz, 1)));
  background: #141119; border: 1px solid #4a3d2e; border-radius: 10px;
  padding: 14px; text-align: center;
  box-shadow: 0 20px 60px rgba(0,0,0,0.7);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
#catcanvas { image-rendering: pixelated; width: 220px; height: 160px; }
#catrevname {
  font-size: 17px; font-weight: 600; letter-spacing: .02em; color: #e8a06a;
  opacity: 0; transform: translateY(5px);
  transition: opacity .28s ease, transform .28s ease;
  min-height: 21px;
}
#catrevname.in { opacity: 1; transform: none; }
#catnotes {
  opacity: 0; transform: translateY(5px);
  transition: opacity .3s ease, transform .3s ease;
  font-size: 12px; color: #cfc3ad; text-align: left; width: 100%;
  line-height: 1.45;
}
#catnotes.in { opacity: 1; transform: none; }
/* one line per thing it can do, with the verb picked out - a row of plain
   sentences all in one colour reads as a paragraph and gets skipped */
.catnote { display: flex; gap: 7px; padding: 2px 0; }
.catnote b { color: #e8c14a; font-weight: 600; flex: 0 0 auto; }
#btn-catok {
  margin-top: 2px; padding: 8px 22px; font-size: 13px; font-weight: 600;
  background: linear-gradient(#a8641f, #7a3a13);
  border: 1px solid #c07a2a; border-top-color: #e8b060; border-bottom-color: #5a2a0e;
  color: #fff3d8; border-radius: 4px; cursor: pointer;
}
#btn-catok:hover { background: linear-gradient(#c07726, #94481a); }
#btn-catok.hidden { display: none; }

/* ============ VILLAGERS TALKING TO EACH OTHER =========================
   The wisp's treatment - small, italic, hard-outlined so it survives any
   ground under it - in a plainer colour, because this is not the light
   beside you being clever, it is two people you happened to walk past.
   Positioned by transform only, so a conversation never touches layout. */
.npctalk {
  position: absolute; left: 0; top: 0; z-index: 6;
  max-width: 250px; padding: 0;
  font-size: 12px; line-height: 1.32; font-style: italic;
  color: #dfe6ef; text-align: center;
  text-shadow: 0 0 4px #0b0a10, 0 1px 0 #0b0a10, 1px 0 0 #0b0a10,
               -1px 0 0 #0b0a10, 0 -1px 0 #0b0a10;
  pointer-events: none; opacity: 0;
  transition: opacity .4s ease-in-out;
  transform: translate(-50%, -100%);
  will-change: transform;
}
.npctalk.on { opacity: 1; }
.npctalk.hidden { display: none; }

/* ============ THE WAGON'S SHELF =======================================
   A locked row is dimmed, not hidden: you are meant to see the thing you
   cannot have yet, and the level it wants written plainly beside it. */
.wagonrow {
  display: flex; align-items: center; gap: 9px;
  padding: 5px 6px; border-radius: 5px;
  border: 1px solid transparent;
}
.wagonrow:hover { background: #221c15; border-color: #4a3d2e; }
/* a line he has already sold: still listed, plainly spent. Kept legible
   rather than hidden - the point of leaving it there is that you can see
   what he HAD, and that the rest of the wagon is still open. */
.wagonrow.sold { opacity: .5; }
.wagonrow.sold .wagonname { text-decoration: line-through; }
.wagonrow.sold button { cursor: default; }
.wagonicon { width: 32px; height: 32px; image-rendering: pixelated; flex: none; }
.wagontext { flex: 1; min-width: 0; text-align: left; }
.wagonname { font-size: 13px; color: #f0e4cc; font-weight: 600; }
.wagonreq { font-size: 11px; color: #a2947c; font-variant-numeric: tabular-nums; }
.wagonrow.locked .wagonicon { filter: grayscale(1); opacity: .45; }
.wagonrow.locked .wagonname { color: #8a8175; }
.wagonrow.locked .wagonreq { color: #b8563c; }
.wagonrow button { padding: 5px 10px; font-size: 12px; flex: none; }
.wagonrow button.dim { opacity: .5; cursor: not-allowed; }

/* ============ THE CAT'S PLATE =========================================
   Sat under your own, narrower and a shade quieter, so it reads as a
   dependant rather than as a second character. Hidden entirely when you
   have no cat - an empty plate is worse than no plate. */
#catplate {
  width: 190px; flex: none; align-self: flex-start;
  background: rgba(12,10,16,0.55);
  border: 1px solid #2f2b3a; border-radius: 5px;
  padding: 4px 6px 5px;
  display: flex; flex-direction: column; gap: 3px;
}
#catplate.hidden { display: none; }
#catplate .charrow { display: flex; align-items: baseline; gap: 6px; }
#catplate .lv { font-size: 10px; color: #a2947c; }
#catname { flex: 1; font-size: 12px; color: #e8dcc4; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#btn-catname { font-size: 10px; color: #8a7f6c; background: none; border: none; cursor: pointer; }
#btn-catname:hover { color: #e8c14a; }
/* ============ THE BAR HAS TO FIT ITS OWN TEXT =======================
   .bar is overflow:hidden with a 16px line-height for the number inside it.
   Cutting the cat's bar down to 9px left the text taller than the box that
   clips it, so the health read as a sliver with its top and bottom sliced
   off - unreadable, which is the one job it has.

   Full height, and the number sized and centred to sit inside it. */
#catplate .bar { height: 15px; margin-bottom: 3px; }
#catplate .bar span { font-size: 10px; line-height: 15px; }
#catbuffs { font-size: 10px; color: #8fbc6d; line-height: 1.35; }
#catbuffs .none { color: #7b7264; font-style: italic; }

/* the ten seconds after you walk in - green, because it is the one pill on
   the screen that means nothing can hurt you */
.pill.grace { background:#16301fc0; border-color:#2f6b45; color:#9fe1bb; }
/* and its opposite: just out of safe ground, you cannot attack but can be hit */
.pill.exitguard { background:#3a2410c0; border-color:#8a5a24; color:#ffd08a; }

/* ------------------------------------------------- the lantern switch ---
   A light source you are still wearing but have put out. It has to read as
   OFF at a glance and still read as WORN - the armour and health it carries
   are unchanged - so it dims rather than empties, and says so in a word. */
/* A lit lantern GLOWS in its own colour and a dark one does not - which is
   what a lantern does, and needs no word written over the artwork. The badge
   that used to sit here said OUT across the icon and read as damage. */
/* ============ THE GLOW SWITCHES, THE SLOT DOES NOT ====================
   The lit state used to repaint the BORDER as well as casting the glow, and
   the shuttered state painted it dull. Between them the slot's own outline
   changed every time the lamp did - so putting the torch out looked like
   taking the torch off, which is the one thing it must never look like.

   The border is the slot saying "there is something equipped here", and that
   stays true whether the thing is alight or not. Only the light comes and
   goes: the halo around the slot, and the glow on the picture itself. */
.slot.lamplit {
  box-shadow: 0 0 7px 1px color-mix(in srgb, var(--lampglow, #ffca7a) 45%, transparent),
              inset 0 0 6px color-mix(in srgb, var(--lampglow, #ffca7a) 22%, transparent);
}
.slot.lamplit img { filter: drop-shadow(0 0 3px var(--lampglow, #ffca7a)); }
/* ============ A SHUTTERED LAMP IS NOT A MISSING ONE ===================
   This used to fade the picture to 42% and drain the colour out of it, and
   that was the right answer when a torch had ONE drawing: greying it was
   the only way to say the fire was out.

   It has two now - burning and out - and the slot swaps between them. So
   the dimming had become the same statement made twice, and the second one
   was wrong: a washed-out ghost at 42% reads as an EMPTY slot, which is the
   opposite of what it is trying to say. You are still carrying the torch.

   The picture stays exactly as drawn, in full colour, and the glow above is
   the whole of the difference. There is no .lampout styling at all any more:
   a shuttered lamp is an ordinary equipped slot that happens not to be
   giving off light. The class is still set, because something may yet want
   to know - but it must not make the slot look emptier. */

/* ------------------------------------------------- somebody has arrived --
   The plate for the handful of things that are EVENTS rather than
   notifications - the wagon from Thornreach, so far. Deliberately quieter
   than the gem gate: no dimmed screen behind it, nothing to click, and it
   leaves on its own. It sits high enough to clear the hotbar and low enough
   not to cover the health plate, because it appears while you are playing
   rather than while you are waiting. */
#arrivegate {
  position: fixed; left: 50%; top: 21%; transform: translate(-50%, -14px);
  z-index: 60; pointer-events: none; opacity: 0;
  transition: opacity .38s ease, transform .38s cubic-bezier(.2, .9, .3, 1);
}
#arrivegate.in { opacity: 1; transform: translate(-50%, 0); }
.arrivebox {
  min-width: 340px; max-width: 520px; padding: 14px 22px 15px;
  text-align: center;
  background: linear-gradient(#171320f2, #0d0a14f2);
  border: 1px solid #6a5a2e;
  box-shadow: 0 0 0 1px #0a0810, 0 10px 34px #000a, inset 0 1px 0 #ffe08a22;
  border-radius: 3px;
}
#arrivetitle {
  font: 700 21px/1.1 ui-monospace, Consolas, monospace;
  letter-spacing: 3px; color: #ffe08a; text-shadow: 0 2px 0 #0008;
}
#arrivewho {
  margin-top: 5px; font-size: 12px; letter-spacing: 2px;
  text-transform: uppercase; color: #c9b98a;
}
#arriveline {
  margin-top: 8px; font-size: 12px; line-height: 1.5; color: #a8a2b4;
}

/* AND A LINE WITH AN EDGE ON IT. Everything a villager says has looked the
   same since they learned to speak, so "back off" read like small talk. Only
   the colour changes - same box, same timing, same place over their head. */
.npctalk.warn {
  color: #ffd23f; border-color: #7a5c12;
  text-shadow: 0 0 6px #ffb32255, 0 2px 0 #0009;
}


/* ============ EVERY WINDOW CLOSES FROM THE SAME CORNER ==================
   The seventeen side panels each had their own × already. Everything built
   in JavaScript - the villager's counter, the bounty board, the leaderboard,
   the weapon and ability pickers, the settings - put a "Close" button at the
   BOTTOM of the content, which on a shopkeeper with a long shelf means
   scrolling to the end of a list to get out of it.

   One button, one corner, on top of whatever the window drew. Sized for a
   mouse rather than for tidiness: a 12px × in the corner of a busy window is
   a thing you hunt for. */
.winx {
  /* STICKY, NOT ABSOLUTE, AND FIRST IN THE BOX. These windows scroll - that
     is the whole complaint - and an absolutely positioned corner is pinned to
     the CONTENT, so it slides up out of sight the moment you scroll down the
     shelf you were trying to escape. Sticky keeps it against the top of the
     viewport of its own scroller, and floating it right takes it out of the
     flow so it does not shove the villager's name down a line. */
  position: sticky; top: 10px; float: right; z-index: 30;
  /* IT NEEDS AIR. Pressed hard into the corner it reads as something that
     fell there rather than something placed. Ten off the top, eight off the
     right edge of the content. The bottom margin is negative by its own
     height plus its top offset, so the float still takes no vertical space
     and the villager's name stays on the first line. */
  margin: 4px 8px -34px 8px;
  width: 26px; height: 26px; padding: 0; line-height: 24px;
  font: 700 16px/24px "Segoe UI", system-ui, sans-serif;
  color: #c8bba8; background: #241f2c; border: 1px solid #4a4055;
  border-radius: 6px; cursor: pointer; text-align: center;
}
.winx:hover { color: #ffd9c0; border-color: #b4553f; background: #3a2430; }
.winx:active { transform: translateY(1px); }
/* the window has to be a positioning parent or the corner is the page's */
.winx-host { position: relative; }

/* ============ NEW ABILITY, AND WHERE TO PUT IT =========================
   A skill level that hands you a spell used to say so in a toast, which
   scrolls off, and grey the shelf - and the thing you actually have to do
   about it is at the bottom of the screen on the bar. So the news is put
   where the action is: a label above the bar with an arrow pointing down
   into it.

   Pinned to the bar row rather than to the window, so it follows the bar
   however that is centred - the same rule the globes beside it follow. It
   never takes the pointer: you are meant to click THROUGH it, at the slot
   it is pointing at. */
/* left is set from JS against the hotbar's own box - see UI.newAbility. The
   row centres four children of different widths, so a flat 50% here points
   at the gap between the recall button and the bar rather than at the bar. */
#abnew {
  position: absolute; bottom: 100%; transform: translateX(-50%);
  margin-bottom: 10px; pointer-events: none; z-index: 6;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  text-align: center; white-space: nowrap;
  animation: abnew-in 260ms ease-out both;
}
#abnew b {
  color: #ffe08a; font: 700 13px/1.3 "Segoe UI", system-ui, sans-serif;
  letter-spacing: .06em; text-shadow: 0 1px 0 #000, 0 0 10px #00000099;
}
#abnew span {
  color: #cfc6b8; font: 500 11px/1.3 "Segoe UI", system-ui, sans-serif;
  text-shadow: 0 1px 0 #000;
}
/* the arrow: a triangle, bobbing towards the bar rather than at it, so it
   reads as "down here" and not as a decoration that happens to be pointy */
#abnew i {
  width: 0; height: 0; margin-top: 2px;
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-top: 9px solid #e8c14a;
  filter: drop-shadow(0 1px 0 #000);
  animation: abnew-bob 1.05s ease-in-out infinite;
}
@keyframes abnew-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
@keyframes abnew-in { from { opacity: 0; transform: translateX(-50%) translateY(-6px); } }
#abnew.going { opacity: 0; transition: opacity 400ms ease; }
@media (prefers-reduced-motion: reduce) {
  #abnew i { animation: none; }
  #abnew { animation: none; }
}


/* ---- the atlas board ----------------------------------------------------
   The canvas is the whole of it. Everything beside it is a legend and two
   buttons, because the board is meant to be looked at rather than operated:
   the reward for a thousand leagues of walking is seeing the shape of the
   world come out of the dark, and a busy panel round it would be arguing
   with that. */
.atlaswrap { display: flex; gap: 16px; align-items: flex-start; padding: 10px 12px 12px; }
/* ============ THE WINDOW HAS TO BE AS BIG AS THE PICTURE ==============
   `.panel` is 320px wide by default and every big window in the game
   declares its own - the vault 620, the bag 420, the character sheet 400.
   The atlas never did, so the largest window in the game opened at 320px
   with `overflow: auto` and you saw about a third of the board through a
   scrollbar. placePanel was already centring it as though it were ~1000px
   wide, which is why it also sat hard against the left edge.

   `max-content` rather than a number, so it hugs whatever the board and the
   column beside it actually come to and cannot drift out of step with them
   the way a hard-coded width would. */
.panel[data-name="atlasboard"] { width: max-content; max-width: calc(calc(100vw / var(--uiz, 1)) - 24px); }
#atlascanvas {
  /* the board draws its own frame, its own wood and its own shadow, so the
     element gets none - a CSS border round a gilt frame is a frame round a
     frame */
  image-rendering: pixelated; display: block;
  width: 700px; height: auto;
  /* ============ AND IT HAS TO FIT BOTH WAYS ==========================
     Three caps, and the smallest wins. The first is the board's own size -
     it is pixel art and blowing it up past 1:1 helps nobody. The second
     leaves room for the column of buttons beside it. The third is the
     HEIGHT cap expressed as a width: the board is 700x790, so multiplying
     the space available by 700/790 gives the widest it can be and still fit
     top to bottom. Doing it this way rather than with max-height is what
     stops the board being squashed - `height: auto` then keeps it square. */
  max-width: min(700px, calc(calc(100vw / var(--uiz, 1)) - 320px), calc((calc(92vh / var(--uiz, 1)) - 96px) * 0.886));
  /* a flex item will not shrink below its own content by default, and the
     canvas's content is 700px wide - without this the caps above are
     ignored the moment the window is small */
  min-width: 0;
  border-radius: 3px; box-shadow: 0 10px 40px #000b;
  cursor: crosshair;
}
.atlasside { display: flex; flex-direction: column; gap: 10px; min-width: 190px; max-width: 230px; }
.atlasnum { font-size: 19px; color: #bfeaff; font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.atlashint { font-size: 12px; color: #8fb6cc; line-height: 1.5; min-height: 3em; }
.atlaskey { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: #8fb6cc; margin-top: 4px; }
.atlaskey span { display: flex; align-items: center; gap: 7px; }
.atlaskey .sw { width: 11px; height: 11px; border-radius: 2px; display: inline-block; }
.atlaskey .sw.placed  { background: #6a9a5a; }
.atlaskey .sw.carried { background: #6fd8ea; }
.atlaskey .sw.walked  { background: #4e6a80; }
.atlaskey .sw.none    { background: #191527; border: 1px solid #262038; }

/* ============ THE CARTOGRAPHER'S SATCHEL ================================
   Owner, 2026-09-27: "new satchel type bag". The belt's sixth cell, which is
   not a belt slot - a picture of one, in the atlas board's gilt so it reads
   as the board's bag and not as a sixth storage bag. See client/js/satchel.js. */
.satchelslot { position: relative; min-width: 38px; min-height: 34px; cursor: pointer; margin-left: 4px;
  border-color: #c8a24a; box-shadow: inset 0 0 0 1px #5c4214; }
.satchelslot:hover { border-color: #e8c060; }
.satchelslot canvas { width: 32px; height: 32px; image-rendering: pixelated; }
.panel[data-name="satchel"] { width: 360px; }
.satchelhead { display: flex; align-items: center; gap: 10px; }
#satchelicon { width: 48px; height: 48px; image-rendering: pixelated; flex: none; }
#satchelcount { font-size: 15px; }
#satchelgroups h4 { margin: 8px 0 2px; font-size: 11px; letter-spacing: 1px; color: #e8c060; text-transform: uppercase; }
#satchelgroups canvas { display: block; image-rendering: pixelated; cursor: pointer; }

/* ============ THE CREATURE CODEX ========================================
   Ported from tools/codex-layout-hall.js, which is where it was designed and
   where the four directions it was chosen from still live. The ONLY change
   made on the way in is the root class - `.lay-hall` became `.codex` - so
   the mockup and the shipped window stay comparable and a change can still
   be tried on the bench before it is brought here.

   Checked before porting: none of the forty-five class names inside this
   block collides with any of the one hundred and sixty-six bare-class
   selectors in the rest of this stylesheet, so nothing here reaches out and
   nothing out there reaches in.

   ---- the window itself -------------------------------------------------
   `.panel` is 320px wide by default and every big window in the game
   declares its own; the atlas learned that the hard way. This one is sized
   by its content and capped to the viewport. */
/* ============ A SIZE OF ITS OWN, NOT ITS CONTENT'S =====================
   `width: max-content` was the atlas's answer and it is wrong here. Opening
   the codex only SENDS the request - the book arrives a round trip later -
   so placePanel measured an EMPTY panel, centred that tiny box, and the
   content then grew rightward out of it. It looked left-aligned and narrow
   because it was placed while it was left-aligned and narrow.

   A stated width fixes both at once: the window is the same size before and
   after the answer lands, so centring it early is centring it correctly.
   Two thirds of the screen, which is what the wall wants. */
.panel[data-name="codex"] {
  width: calc(66vw / var(--uiz, 1));
  min-width: min(940px, calc(calc(100vw / var(--uiz, 1)) - 24px));
  max-width: calc(calc(100vw / var(--uiz, 1)) - 24px);
}
/* TWICE THE WIDTH. The book was capped at 1140, which on a wide screen left
   the wall four mounts across with the rest of the desk empty either side -
   and the whole point of the wall is seeing a band at a glance. The panel's
   own `max-width: calc(100vw - 24px)` above still keeps it on the screen, so
   this is a ceiling rather than a size. */
/* and the book fills whatever the window turned out to be */
.panel[data-name="codex"] > .codex { max-width: none; }

/* ============ LAMPLIGHT, NOT DAYLIGHT ================================
   The Register's page is near-white cream. Dropped into a game whose
   ground is #0d0c10 that is a lit slab, and every sprite on it has to
   fight the paper. So the book is kept and the hour is changed: this is
   the same vellum, the same ink, the same red ribbon, seen by a lamp.

   Which means the ramp INVERTS relative to the Register - the page is the
   dark warm surface and the ink is the pale warm mark - while every hue
   stays where it was. Nothing here is grey any more: the old violet-grey
   furniture (#14131b, #34313f, #8b8898) is gone, replaced tan for tan at
   the same weight, so the layout reads identically and only its
   temperature has moved.

   Every colour is a token. The next note on this will be "warmer" or
   "darker", and that has to be six lines, not two hundred. */
.codex {
  --ground:   #0d0c10;   /* the desk the book lies on - the game's own */
  /* ---- A SLIGHT SEPIA, NOT A BROWN --------------------------------
     The first pass took the Register's tan all the way in and the whole
     book went the colour of a cigar. Pulled most of the way back to the
     game's own dark, keeping only the warmth: these are warm greys, not
     browns - the hue is still off-neutral but the saturation is a small
     fraction of what it was. */
  --pap:      #1a1917;   /* the page */
  --pap-lit:  #232120;   /* a raised leaf, a hover */
  --pap-sunk: #151412;   /* pressed in */
  --pap-deep: #0f0e0d;   /* the sunken well a sprite stands in */
  --rule:     #38342e;   /* every hairline in the book */
  --rule-lit: #46413a;   /* the lit edge of a raised leaf */

  --ink:      #e6e1d6;   /* what is written */
  --ink-dim:  #9c958a;
  --ink-faint:#625c53;

  --gold:     #e8a04a;   /* the one accent, unchanged - it was always warm */
  --hot:      #ffd8a0;
  --good:     #6fc85a;   /* the Cabinet's green - what is finished */
  --blood:    #e04a5e;

  --ribbon:     #7e2028; /* muted from the Register's #a8323a */
  --ribbon-lit: #9c3038;
  --ribbon-dark:#4a1018;
  --ribbon-fold:#3a0d13;
  --wax:        #8f2530;
  --board:      #221e1a; /* the boards the leaves are bound into */
  --board-lit:  #35302a;

  color: var(--ink); font-size: 13px;
  font-family: "Segoe UI", system-ui, sans-serif;
  /* ============ A BOOK IS AN OBJECT, NOT A BACKDROP ================
     It was filling whatever window it was given, which makes it the room
     rather than a thing in the room. Capped and centred, the way every
     other window in this game declares its own width - the vault 620, the
     bag 420, the atlas max-content. */
  max-width: 1120px; margin: 0 auto;
}
.codex * { box-sizing: border-box; }

/* ------------------------------------------------------------- board --- */
.codex .board {
  border: 1px solid var(--rule); background: var(--pap); padding: 11px 13px 12px;
  display: flex; gap: 20px; align-items: flex-end; flex-wrap: wrap;
  box-shadow: inset 1px 1px 0 var(--rule-lit), inset -1px -1px 0 var(--pap-deep);
}
.codex .board h2 {
  margin: 0; font-size: 16px; font-weight: 600; color: var(--gold);
  letter-spacing: .26em; text-transform: uppercase;
}
.codex .board .sub { margin: 5px 0 0; font-size: 11.5px; color: var(--ink-dim); }
.codex .board .sub b { color: var(--ink); font-weight: 600; }

/* ---- the ribbon, carrying whatever the book is open at ---------------
   THE NOTCHED ENDS ARE CUT IN STEPS, NOT SLANTS. A clip-path diagonal is
   the one thing a browser will anti-alias, and this game does not do soft
   edges anywhere - a blurred diagonal on the loudest ornament on the page
   would make the softest thing the first thing seen. Five-pixel treads
   read as drawn pixels and stay hard at any window size. */
.codex .ribwrap { position: relative; margin: 9px 0 0; }
.codex .ribbon {
  position: relative; z-index: 2;
  background: var(--ribbon); color: #ffe8cf;
  padding: 6px 26px; display: flex; align-items: baseline; gap: 10px;
  box-shadow: inset 0 1px 0 var(--ribbon-lit), inset 0 -1px 0 var(--ribbon-dark);
  clip-path: polygon(
    0 0, 100% 0,
    100% 20%, calc(100% - 5px) 20%, calc(100% - 5px) 40%, calc(100% - 10px) 40%,
    calc(100% - 10px) 60%, calc(100% - 5px) 60%, calc(100% - 5px) 80%, 100% 80%,
    100% 100%, 0 100%,
    0 80%, 5px 80%, 5px 60%, 10px 60%, 10px 40%, 5px 40%, 5px 20%, 0 20%);
}
.codex .ribbon .rtitle {
  font-size: 12.5px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.codex .ribbon .rmeta {
  margin-left: auto; font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
  color: #f3c3a6; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.codex .rtail { position: absolute; top: 8px; width: 13px; height: 22px; background: var(--ribbon-fold); z-index: 1; }
.codex .rtail.l { left: -13px;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 9px 100%, 9px 87%, 4px 87%, 4px 75%, 0 75%, 0 62%); }
.codex .rtail.r { right: -13px;
  clip-path: polygon(0 0, 100% 0, 100% 62%, calc(100% - 4px) 75%, calc(100% - 4px) 87%, calc(100% - 9px) 87%, calc(100% - 9px) 100%, 0 100%); }

/* ---- the whole-collection readout ---------------------------------- */
.codex .tally { display: flex; align-items: flex-end; gap: 13px; margin-left: auto; }
.codex .tally .big {
  font-size: 21px; color: var(--gold); font-variant-numeric: tabular-nums;
  letter-spacing: .04em; line-height: 1;
}
.codex .tally .of {
  font-size: 10px; letter-spacing: .17em; text-transform: uppercase;
  color: var(--ink-faint); margin-top: 5px;
}

/* ============ THE THREE-STATE BAR, FROM THE CABINET ==================
   The old one was two browns on brown and said very little. This is the
   Cabinet's, which carries three facts in one strip and reads at a glance
   at any size: GREEN for what is finished, GOLD for what is begun but not
   done, and the dark ground for what has never been met. Each lit segment
   takes a one-pixel brighter line along its top, which is what keeps it
   from looking like a flat web progress bar. */
.codex .bar3 {
  display: flex; height: 6px; background: var(--pap-deep);
  border: 1px solid var(--rule); overflow: hidden;
}
.codex .bar3 i { display: block; height: 100%; }
.codex .bar3 i.d { background: var(--good); box-shadow: inset 0 1px 0 #a6e894; }
.codex .bar3 i.p { background: var(--gold); box-shadow: inset 0 1px 0 var(--hot); }
.codex .bar3.thin { height: 4px; }
/* a plain single-value bar, for a drop chance - a quantity, not a state */
.codex .pbar { height: 4px; background: var(--pap-deep); border: 1px solid var(--rule); }
.codex .pbar i { display: block; height: 100%; background: var(--gold); }

/* ============ AND THE MARK EVERYONE REACTS TO =======================
   DRAWN, NOT TYPED - the Cabinet's note on this is right and worth
   keeping: a tick out of whatever symbol font the machine happens to have
   is an anti-aliased vector glyph at 10px sitting beside pixel art, at a
   weight and a baseline nobody chose. This is two borders on a rotated
   box: it takes the palette variable directly, lands on whole pixels, and
   no font substitution can redraw it. */
/* ---- and it gets the corner the eye lands on ------------------------
   Top-right. The plate's four corners are all spoken for and which mark
   gets which is not arbitrary: a tile is read from the top, so the corner
   that is looked at first belongs to the thing you most want to know at a
   glance - is this one finished. The level badge, which you only read once
   you have decided to care about the creature, took the bottom-right in
   exchange. They were the other way round and it read worse. */
.codex .tick {
  position: absolute; right: 6px; top: 6px; width: 9px; height: 5px;
  border-left: 2px solid var(--good); border-bottom: 2px solid var(--good);
  transform: rotate(-45deg); z-index: 2;
}
/* and the eight-point star a Great Beast wears */
.codex .bstar {
  position: absolute; left: 4px; top: 4px; width: 9px; height: 9px; background: var(--gold);
  clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
  z-index: 2;
}

/* ---- the category chips, also the Cabinet's ------------------------- */
.codex .chips { display: flex; gap: 5px; flex-wrap: wrap; }
.codex .chip {
  display: flex; align-items: center; gap: 6px; background: var(--pap-lit);
  border: 1px solid var(--rule); color: var(--ink-dim); font: inherit;
  font-size: 11px; cursor: pointer; padding: 5px 9px; border-radius: 0;
  letter-spacing: .04em;
}
.codex .chip:hover { background: #2c2926; color: var(--ink); }
.codex .chip.on { background: #2f2718; border-color: var(--gold); color: var(--hot); }
.codex .chip .cdot { width: 7px; height: 7px; flex: none; background: var(--rule); }
.codex .chip .cdot.d { background: var(--good); }
.codex .chip .cdot.p { background: var(--gold); }
.codex .chip .cdot.n { background: #46413a; }
.codex .chip .cn { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.codex .chip.on .cn { color: var(--gold); }

/* --------------------------------------------------- bars and views --- */
.codex .bars { display: flex; gap: 9px; margin: 9px 0; flex-wrap: wrap; align-items: center; }
.codex .views { display: flex; gap: 6px; margin-left: auto; }
.codex .vbtn {
  background: var(--pap-lit); border: 1px solid var(--rule); color: var(--ink-dim);
  font: inherit; cursor: pointer; padding: 7px 13px; border-radius: 0;
  font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
  box-shadow: inset 1px 1px 0 var(--rule-lit), inset -1px -1px 0 var(--pap-deep);
}
.codex .vbtn:hover { background: #423625; color: var(--ink); }
.codex .vbtn.on { background: #5c3a17; border-color: var(--gold); color: #ffe3b8;
  box-shadow: inset 1px 1px 0 #8f5a26, inset -1px -1px 0 #2a1a0c; }
.codex .find { position: relative; display: flex; }
.codex .find input {
  background: var(--pap-sunk); border: 1px solid var(--rule); color: var(--ink);
  font: inherit; padding: 6px 9px; width: 200px; border-radius: 0;
  box-shadow: inset 1px 1px 0 var(--pap-deep);
}
.codex .find input:focus { outline: none; border-color: var(--ribbon); background: #322718; }
.codex .find input::placeholder { color: var(--ink-faint); }

/* --------------------------------------------------------- the frame --- */
.codex .frame { display: grid; grid-template-columns: 46px 186px minmax(0, 1fr); gap: 0; }

/* ---- the spine: the three shelves, as tabs cut into the binding ------
   This replaces three buttons in a row, and it is the single device that
   says BOOK loudest - the shelves stop being a toolbar and become the
   parts of a bound volume you put a thumb into. */
.codex .spine {
  background: var(--board);
  box-shadow: inset 4px 0 0 #150f0a, inset -4px 0 0 #150f0a, inset 0 0 0 1px var(--board-lit);
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 14px 0; height: 524px;
}
.codex .headband {
  width: 30px; height: 8px; flex: 0 0 8px;
  background: repeating-linear-gradient(90deg, var(--wax) 0 3px, #a08a5e 3px 6px);
  box-shadow: inset 0 0 0 1px #150f0a;
}
.codex .stab {
  writing-mode: vertical-rl; transform: rotate(180deg);
  background: #2e2217; color: #b79a6c; border: 1px solid var(--board-lit);
  padding: 14px 5px; cursor: pointer; border-radius: 0; font-family: inherit;
  font-size: 10.5px; letter-spacing: .24em; text-transform: uppercase;
  box-shadow: inset 1px 1px 0 #443121, inset -1px -1px 0 #1a120c;
}
.codex .stab:hover { color: var(--hot); border-color: #6b5033; background: #38291b; }
.codex .stab.on {
  background: #5c3a17; color: #ffe3b8; border-color: var(--gold);
  box-shadow: inset 1px 1px 0 #8f5a26, inset -1px -1px 0 #2a1a0c;
}
.codex .stab .n { color: #8a7350; letter-spacing: .1em; }
.codex .stab.on .n { color: #d8a866; }
/* each tab carries its own completion, the way the Cabinet's shelf tabs
   do - so you can see which of the three shelves is behind without
   opening it. Turned on its side with the tab. */
.codex .stabwrap { display: flex; align-items: stretch; gap: 3px; }
.codex .stabwrap .bar3 {
  width: 5px; height: auto; flex-direction: column-reverse; align-self: stretch;
}
.codex .stabwrap .bar3 i { width: 100%; height: auto; }

/* ---- the index down the left page ---------------------------------- */
.codex .rail {
  border: 1px solid var(--rule); border-left: 0; background: var(--pap);
  overflow-y: auto; height: 524px; align-self: start;
}
.codex .railhead {
  padding: 8px 10px; font-size: 9.5px; letter-spacing: .19em; text-transform: uppercase;
  color: var(--ink-faint); border-bottom: 1px solid var(--rule); background: var(--pap-sunk);
  position: sticky; top: 0; z-index: 2;
}
.codex .railrow {
  display: block; width: 100%; text-align: left; background: var(--pap); color: var(--ink-dim);
  border: 0; border-bottom: 1px solid var(--rule); padding: 6px 9px 7px; cursor: pointer;
  font: inherit;
}
.codex .railrow:hover { background: var(--pap-lit); }
.codex .railrow.on { background: #3d2a1c; box-shadow: inset 3px 0 0 var(--ribbon); }
.codex .railrow .t { display: flex; align-items: baseline; gap: 6px; }
.codex .railrow .t b { font-weight: 600; font-size: 11.5px; color: var(--ink); }
.codex .railrow.on .t b { color: var(--hot); }
.codex .railrow .t s {
  text-decoration: none; margin-left: auto; font-size: 10px; color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}
.codex .railrow .bar3 { margin-top: 5px; }

/* ---- the right page ------------------------------------------------ */
.codex .stage {
  border: 1px solid var(--rule); border-left: 0; height: 524px;
  overflow-y: auto; overflow-x: hidden;
  background-color: var(--pap-deep);
  /* paper grain: two hard-stepped rules, no blur, the same trick the wall
     used for plaster but warm and finer */
  background-image:
    repeating-linear-gradient(90deg, transparent 0 3px, #ffffff05 3px 4px),
    repeating-linear-gradient(0deg, transparent 0 3px, #00000030 3px 4px);
}
.codex .stage.list { background-image: none; background-color: var(--pap); }
.codex .empty { padding: 30px 16px; color: var(--ink-faint); font-size: 12px; text-align: center; }

/* ============ WALL VIEW ============================================== */
.codex .band { border-bottom: 1px solid var(--rule); }
.codex .bandhead {
  position: sticky; top: 0; z-index: 3; display: flex; align-items: center; gap: 11px;
  padding: 7px 12px 8px; background: var(--pap); border-bottom: 1px solid var(--rule);
  cursor: pointer; box-shadow: inset 0 1px 0 var(--rule-lit);
}
.codex .bandhead:hover { background: var(--pap-lit); }
.codex .bandhead .car { color: var(--ink-faint); font-size: 9px; width: 9px; flex: none; }
.codex .bandhead h4 {
  margin: 0; font-size: 13px; font-weight: 600; color: var(--gold);
  letter-spacing: .17em; text-transform: uppercase; flex: none;
}
.codex .bandhead .note {
  color: var(--ink-faint); font-size: 11.5px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; min-width: 0; font-style: italic;
}
.codex .bandhead .fill { margin-left: auto; display: flex; align-items: center; gap: 9px; flex: none; }
.codex .bandhead .fill .n {
  font-size: 11px; color: var(--ink-dim); letter-spacing: .07em; font-variant-numeric: tabular-nums;
}
.codex .bandhead .fill .bar3 { width: 94px; }
.codex .band.boss .bandhead h4 { color: var(--hot); }

.codex .grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 10px; padding: 13px 12px 17px; align-items: start;
}
.codex .band.collapsed .grid { display: none; }
.codex .band.boss .grid { grid-template-columns: repeat(auto-fill, minmax(176px, 1fr)); }

.codex .mount { cursor: pointer; }
/* ============ A SUNKEN WELL, SO THE COLOUR IS THE CREATURE'S ========
   The plate was a raised warm tile, and a warm surround casts its own
   colour over everything standing on it - a green slime on tan is not the
   green that slime is. The Cabinet had this right: the plate is a well
   pressed INTO the page, near black and all but neutral, so nothing
   reaches the sprite and every creature and every piece of gear is drawn
   in exactly its own palette. */
.codex .mount .plate2 {
  position: relative; height: 96px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--rule); background: var(--pap-deep);
  box-shadow: inset 1px 1px 0 #08070b, inset -1px -1px 0 var(--rule);
}
.codex .mount img { image-rendering: pixelated; display: block; }
.codex .mount .hook {
  position: absolute; top: -1px; left: 50%; margin-left: -6px;
  width: 12px; height: 3px; background: #6b5836;
}
/* the level goes bottom-right so the tick can have the top-right corner:
   the eye goes to the top of a tile first, and the finished-mark is what
   should be waiting there. The badge is edged on the two sides that face
   INTO the plate, which at this corner is its left and its top. */
.codex .mount .lv {
  position: absolute; right: 0; bottom: 0; font-size: 9px; color: var(--ink-dim);
  padding: 1px 4px; background: var(--pap); letter-spacing: .06em;
  border-left: 1px solid var(--rule); border-top: 1px solid var(--rule);
  font-variant-numeric: tabular-nums;
}
.codex .mount .q { font-size: 30px; color: var(--rule); line-height: 1; }

/* ---- three wax seals, not three pips --------------------------------
   Cut as a stepped octagon rather than a border-radius circle: this game
   anti-aliases nothing, and a soft round dot beside hard pixel art is the
   one shape that would give the whole page away as a web document. */
.codex .seals { position: absolute; left: 5px; bottom: 5px; display: flex; gap: 3px; }
.codex .seals i {
  width: 7px; height: 7px; background: var(--pap-sunk);
  box-shadow: inset 0 0 0 1px var(--rule);
  clip-path: polygon(2px 0, 5px 0, 7px 2px, 7px 5px, 5px 7px, 2px 7px, 0 5px, 0 2px);
}
.codex .seals i.lit { background: var(--wax); box-shadow: inset 0 1px 0 #c2444d; }

.codex .mount .nameplate {
  border: 1px solid var(--rule); border-top: 0; background: var(--pap); padding: 5px 5px 0;
}
.codex .mount .pn {
  font-size: 10.5px; color: var(--ink); line-height: 1.25; min-height: 25px;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.codex .mount .ps {
  font-size: 9px; color: var(--ink-faint); letter-spacing: .13em; text-transform: uppercase;
  margin: 2px 0 4px;
}
.codex .mount .sliver { height: 3px; margin: 0 -5px; background: var(--pap-deep); border-top: 1px solid var(--rule); }
.codex .mount .sliver i { display: block; height: 100%; background: #7a5c31; }
.codex .mount .sliver.full i { background: var(--gold); }
.codex .mount:hover .plate2 { border-color: #7a6544; }
.codex .mount:hover .pn { color: var(--hot); }
.codex .mount.sel .plate2 { border-color: var(--gold); box-shadow: inset 1px 1px 0 #8f5a26, inset -1px -1px 0 #241a10; }
.codex .mount.sel .nameplate { border-color: var(--gold); background: #3d2a1c; }
.codex .mount.sel .pn { color: var(--hot); }

/* an empty hook: pressed into the page, the shape shrouded, a ? over it */
.codex .mount[data-s="0"] .plate2 {
  background: var(--pap-sunk); border-color: #3a3021;
  box-shadow: inset 1px 1px 0 #120d07, inset -1px -1px 0 #382d1e;
}
.codex .mount[data-s="0"] .hook { background: #3a3021; }
/* ============ THE SILHOUETTE MUST NOT BE DRAGGABLE ====================
   The three rules that hide an undiscovered creature all do it with a
   filter, and a filter does not reach the browser's drag image - which is
   drawn from the source bitmap. Dragging a black silhouette therefore lifted
   the monster in full colour.

   codex.js sets draggable=false on every picture it builds, which is the
   real fix; this is the belt to that pair of braces, and it also covers the
   text, so dragging across a card selects nothing instead of highlighting
   half the book. */
.codex img { -webkit-user-drag: none; user-drag: none; }
.codex .mount, .codex .reg, .codex .case { user-select: none; }
.codex .mount[data-s="0"] img { filter: grayscale(1) brightness(0) invert(21%) sepia(28%); opacity: .85; }
.codex .mount[data-s="0"] .nameplate { background: var(--pap-sunk); border-color: #3a3021; }
.codex .mount[data-s="0"] .pn { color: #6b5c42; letter-spacing: .1em; }
.codex .mount[data-s="0"]:hover .pn { color: var(--ink-dim); }

.codex .mount.elite { grid-column: span 2; }
.codex .mount.elite .plate2 { border-color: #6b5033; }
.codex .mount.elite .pn { color: var(--hot); }
.codex .mount.elite[data-s="0"] .plate2 { border-color: #453722; }
.codex .mount.elite[data-s="0"] .pn { color: #6b5c42; }
.codex .band.boss .mount .plate2 { height: 152px; border-color: #8a6a34; }
.codex .band.boss .mount .pn { font-size: 11px; color: var(--hot); }
.codex .band.boss .mount[data-s="0"] .plate2 { border-color: #4e3f28; }
.codex .band.boss .mount[data-s="0"] .pn { color: #6b5c42; }

/* ============ LIST VIEW - the ledger, ruled on the same paper ======== */
.codex .reg { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.codex .reg th {
  position: sticky; top: 0; z-index: 2; background: var(--pap-sunk); text-align: right;
  font-size: 9px; letter-spacing: .15em; text-transform: uppercase; color: var(--ink-dim);
  font-weight: 600; padding: 8px 8px 7px; border-bottom: 1px solid var(--rule);
  cursor: pointer; white-space: nowrap;
}
.codex .reg th:hover { color: var(--ink); background: #2a2216; }
.codex .reg th.lead { text-align: left; }
.codex .reg th.on { color: var(--gold); }
.codex .reg th .car { font-size: 8px; margin-left: 3px; }
.codex .reg td {
  padding: 0 8px; height: 24px; border-bottom: 1px solid #3a3021;
  text-align: right; white-space: nowrap; font-size: 11.5px; color: var(--ink-dim);
}
.codex .reg tr:nth-child(even) td { background: #2a2217; }
.codex .reg tr:hover td { background: var(--pap-lit); }
.codex .reg tr.sel td { background: #3d2a1c; box-shadow: inset 0 1px 0 var(--gold), inset 0 -1px 0 var(--gold); }
.codex .reg td.lead { text-align: left; }
.codex .reg td.num { color: var(--ink-faint); font-size: 10px; width: 38px; }
.codex .reg .who { display: flex; align-items: center; gap: 8px; min-width: 0; }
.codex .reg .who img { image-rendering: pixelated; flex: none; }
.codex .reg .who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; color: var(--ink); }
.codex .reg .cl { color: var(--ink-faint); font-size: 10.5px; letter-spacing: .06em; }
.codex .reg tr.unk td { color: #5c4e39; }
.codex .reg tr.unk .who img { filter: grayscale(1) brightness(0) invert(21%) sepia(28%); opacity: .85; }
.codex .reg tr.unk .who span { color: #6b5c42; letter-spacing: .13em; }
.codex .reg tr.unk td.dash { color: #453a29; }

.codex .reg .cell { position: relative; display: block; padding: 3px 0 4px; }
.codex .reg .cell u {
  position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: #3a3021; text-decoration: none;
}
.codex .reg .cell u i { display: block; height: 100%; background: #6b5836; }
.codex .reg td.sorted .cell u i { background: var(--gold); }
.codex .reg td.pcell { width: 92px; }
.codex .reg .pc { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
.codex .reg .pc .bar3 { width: 46px; }
.codex .reg .pc b { font-weight: 400; font-size: 10.5px; color: var(--ink-dim); width: 30px; text-align: right; }
.codex .reg tr.unk .pc b { color: #5c4e39; }
/* a class heading in the register, ruled off like a chapter */
.codex .reg .grouprow td {
  background: var(--pap-sunk); color: var(--gold); font-size: 10px; letter-spacing: .17em;
  text-transform: uppercase; height: 26px;
  border-bottom: 1px solid var(--rule); border-top: 1px solid var(--rule);
  box-shadow: inset 3px 0 0 var(--ribbon);
}
.codex .reg .grouprow td s { text-decoration: none; color: var(--ink-faint); letter-spacing: .07em; margin-left: 8px; }

/* ============ THE CASE - one entry, shared by both views =============
   The panel itself is the Cabinet's, which was the best thing any of the
   four drafts produced: a ladder of three numbered rungs, then the field
   measurements in two columns, then the named blow, then what is written,
   then the spoils. Ported whole rather than approximated, and re-dressed
   in the book's tokens.

   Two of its decisions are load-bearing and are kept exactly:

   * A SHUT RUNG SHOWS ITS PRICE ONLY ONCE YOU HAVE MET THE THING. The
     thresholds fall straight out of level and the boss flag, so on a
     creature you have never killed "0 / 2" says Great Beast and "0 / 25"
     says common as muck. The mount refuses to say which; the panel must
     not say it either. Rung I is always one kill and gives nothing away.
   * THE LORE COMES BEFORE THE DROPS. Every other block is a fixed height;
     the drop table is the one that runs from nothing to ten rows, and with
     it in the middle a well-laden creature pushed the lore - the whole
     reward for a full record - clean off the bottom. */
.codex .case {
  border: 1px solid var(--gold); background: var(--pap); margin-top: 9px;
  display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 14px; padding: 12px;
  box-shadow: inset 1px 1px 0 var(--rule-lit), inset -1px -1px 0 var(--pap-deep);
}
.codex .case .pic {
  border: 1px solid var(--rule); background: var(--pap-deep); height: 168px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: inset 1px 1px 0 #2e2418;
}
.codex .case .pic img { image-rendering: pixelated; }
.codex .case .pic.unk img { filter: grayscale(1) brightness(0) invert(21%) sepia(28%); opacity: .85; }
.codex .case h3 {
  margin: 0 0 2px; font-size: 15px; color: var(--hot); font-weight: 600; letter-spacing: .05em;
}
.codex .case .meta { font-size: 11px; color: var(--ink-faint); letter-spacing: .1em; text-transform: uppercase; }
.codex .case .close {
  float: right; background: var(--pap-lit); border: 1px solid var(--rule); color: var(--ink-dim);
  font: inherit; font-size: 11px; cursor: pointer; padding: 2px 8px; border-radius: 0;
}
.codex .case .close:hover { color: var(--ink); background: #423625; }

/* ---- a section heading with the rule running off the end of it ------- */
.codex .sect { margin-top: 10px; }
.codex .sect > h4 {
  margin: 0 0 6px; display: flex; align-items: center; gap: 9px;
  font-size: 9px; font-weight: 600; letter-spacing: .21em; text-transform: uppercase;
  color: var(--ink-faint); white-space: nowrap;
}
.codex .sect > h4::after { content: ''; flex: 1; height: 1px; background: var(--rule); }

/* ---- the ladder ------------------------------------------------------ */
.codex .rung {
  display: flex; gap: 9px; align-items: flex-start; padding: 5px 8px;
  border-left: 2px solid var(--rule); background: var(--pap-lit); margin-bottom: 2px;
}
.codex .rung.open { border-left-color: var(--good); background: #2b2a18; }
.codex .rung.next { border-left-color: var(--gold); background: #33291a; }
.codex .rung .rn {
  color: var(--ink-faint); font-size: 10px; width: 20px; flex: none;
  letter-spacing: .06em; padding-top: 1px;
}
.codex .rung.open .rn { color: var(--good); }
.codex .rung.next .rn { color: var(--gold); }
.codex .rung .rw { flex: 1; min-width: 0; }
.codex .rung .rt { font-size: 11.5px; color: var(--ink-dim); }
.codex .rung.open .rt, .codex .rung.next .rt { color: var(--ink); }
.codex .rung .ro { font-size: 10.5px; color: var(--ink-faint); margin-top: 1px; }
.codex .rung .rk {
  font-size: 10.5px; color: var(--ink-faint); flex: none;
  font-variant-numeric: tabular-nums; padding-top: 1px;
}
.codex .rung.open .rk { color: var(--good); }
.codex .rung.next .rk { color: var(--gold); }
.codex .rung .purse {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 4px;
  font-size: 10.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
}
.codex .rung .purse.got { color: var(--gold); }
.codex .rung .purse i.coin {
  width: 7px; height: 7px; background: var(--ink-faint);
  clip-path: polygon(2px 0, 5px 0, 7px 2px, 7px 5px, 5px 7px, 2px 7px, 0 5px, 0 2px);
}
.codex .rung .purse.got i.coin { background: var(--gold); }
.codex .rung .rbar { margin-top: 5px; height: 4px; background: var(--pap-deep); border: 1px solid var(--rule); }
.codex .rung .rbar i { display: block; height: 100%; background: var(--gold); }

/* ---- the measurements, two columns of label and value ---------------- */
.codex .stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--rule); border: 1px solid var(--rule);
}
.codex .stats div {
  display: flex; justify-content: space-between; gap: 6px; padding: 4px 7px;
  background: var(--pap-lit); font-size: 11.5px;
}
.codex .stats div u { text-decoration: none; color: var(--ink-dim); }
.codex .stats div b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.codex .stats div b.hp { color: #e8b0a0; }
.codex .stats div b.dm { color: #e0707a; }
.codex .stats div b.xp { color: var(--gold); }

/* ---- a block you have not earned ------------------------------------- */
.codex .lock { display: flex; align-items: center; gap: 10px; padding: 2px 0; }
.codex .lock .lbox {
  width: 30px; height: 30px; flex: none; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--rule); background: var(--pap-sunk); color: var(--rule);
  font-size: 15px; box-shadow: inset 1px 1px 0 #120d07;
}
.codex .lock .ll { font-size: 11px; color: var(--ink-faint); line-height: 1.45; }
.codex .lockcost {
  margin-top: 5px; font-size: 10.5px; color: var(--gold);
  letter-spacing: .04em; font-variant-numeric: tabular-nums;
}
.codex .lockcost b { color: var(--hot); font-weight: 600; }
.codex .lockcost.cold { color: var(--ink-faint); }
.codex .lockcost.cold b { color: var(--ink-dim); }

/* ---- the one named blow ---------------------------------------------- */
.codex .spec { background: var(--pap-lit); border: 1px solid var(--rule); padding: 8px; }
.codex .spec .sname {
  display: flex; align-items: center; gap: 7px; font-size: 12.5px;
  color: var(--ink); font-weight: 600;
}
.codex .spec .swatch { width: 10px; height: 10px; flex: none; border: 1px solid #120d07; }
.codex .spec .smeta { color: var(--ink-dim); font-size: 11px; margin-top: 3px; font-variant-numeric: tabular-nums; }
.codex .tags { margin-top: 5px; }
.codex .tag {
  display: inline-block; font-size: 9px; letter-spacing: .12em; text-transform: uppercase;
  padding: 1px 5px; margin-right: 4px; border: 1px solid var(--rule); color: var(--ink-dim);
}
.codex .tag.bleed { color: #e0707a; border-color: #6b2a30; }
.codex .tag.burn { color: #e8a05c; border-color: #6b4520; }
.codex .tag.poison { color: var(--good); border-color: #3f5628; }
.codex .tag.slow, .codex .tag.root, .codex .tag.knock { color: #9db4d8; border-color: #33415c; }

/* ---- what is written -------------------------------------------------- */
.codex .lore {
  font-size: 11.5px; color: var(--ink); line-height: 1.6; font-style: italic;
  border-left: 2px solid var(--ribbon); padding-left: 9px;
}
.codex .lore.none { color: var(--ink-faint); border-left-color: var(--rule); }

/* ---- the spoils -------------------------------------------------------- */
.codex .drop {
  display: flex; align-items: center; gap: 8px; font-size: 11.5px;
  padding: 3px 0; border-bottom: 1px solid #3a3021;
}
.codex .drop:last-child { border-bottom: 0; }
.codex .drop img { image-rendering: pixelated; flex: none; }
.codex .drop .dot {
  width: 20px; height: 20px; flex: none; border: 1px solid var(--rule); background: var(--pap-sunk);
}
.codex .drop .dn { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.codex .drop .dq { color: var(--ink-faint); font-variant-numeric: tabular-nums; flex: none; }
.codex .drop .dc {
  color: var(--ink-dim); font-variant-numeric: tabular-nums; flex: none;
  width: 44px; text-align: right;
}
.codex .drop .dc.rare { color: var(--gold); }
/* who buys it / who sells it: a price can run to six figures, and money is
   the purse's yellow wherever it appears */
.codex .drop.trade .dc.gp { color: var(--gold); width: auto; min-width: 44px; }
.codex .drop.trade .dq { max-width: 42%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* LOOTED FROM (owner, 2026-10-11 - client/js/codex.js): what YOU have had
   off a creature is the finished green, on the item's page and on the
   creature's own drop table; a row that opens a creature lights on hover */
.codex .drop .lt {
  color: var(--good); font-variant-numeric: tabular-nums; flex: none; font-size: 10.5px;
  font-family: ui-monospace, Consolas, "Lucida Console", monospace;
}
.codex .drop.mine .dn { color: #d9f0cf; }
.codex .drop.mine::before {
  content: ''; flex: none; width: 7px; height: 4px; margin: 0 1px 2px 0;
  border-left: 2px solid var(--good); border-bottom: 2px solid var(--good); transform: rotate(-45deg);
}
.codex .drop.from .lt { font-size: 11.5px; min-width: 38px; text-align: right; }
.codex .drop.from.go { cursor: pointer; }
.codex .drop.from.go:hover { background: var(--pap-lit); }
.codex .drop.from.go:hover .dn { color: var(--hot); text-decoration: underline; text-underline-offset: 2px; }
.codex .lootnote { margin: 6px 0 2px; font-size: 10.5px; }
.codex .more {
  margin-top: 5px; background: var(--pap-lit); border: 1px solid var(--rule); color: var(--ink-dim);
  font: inherit; font-size: 10.5px; cursor: pointer; padding: 3px 8px; border-radius: 0;
}
.codex .more:hover { color: var(--ink); background: #423625; }

/* ============ THE ATLAS FRAME AND THE ASTROLABE CORNER ===============
   Two borrowings, so the codex sits in the same room as the rest of the
   game rather than beside it.

   FROM THE ATLAS BOARD - the gilt. It is drawn on that canvas as four
   nested passes, each one light along the top and left and dark along the
   bottom and right:

       lay(15, 7, '#8a6420', '#5c4214');   lay(15, 4, '#c8a24a', '#7a5a1c');
       lay(9,  3, '#ffe9b0', '#9a7628');   lay(4,  1, '#e8c060', '#6a4c18');

   That two-tone-per-edge is the whole trick - it is what makes the frame
   sit ON the board instead of being painted on it - and the same four
   passes are rebuilt here in the same order out of inset shadows. An
   earlier shadow paints over a later one, so they are listed innermost
   first and the bands come out 1 / 4 / 8 / 11 exactly as the canvas draws
   them. The board underneath is the same wood, planked with hard steps
   because nothing in this game is allowed to blur.

   FROM THE SKILL CHART - the corner. Its reading card is cornered "the way
   an astrolabe plate is cornered": four 9px brackets held 3px off the
   edge, on the corners only, never a full second border. Kept at that size
   and that inset, warmed from the chart's silver to the book's gilt so it
   belongs to this page. */
.codex .gilt {
  padding: 12px;
  background-color: #4b3524;
  background-image:
    repeating-linear-gradient(0deg, rgba(20,12,7,.55) 0 2px, transparent 2px 62px),
    repeating-linear-gradient(0deg, rgba(30,18,10,.13) 0 1px, transparent 1px 11px);
  box-shadow:
    inset 1px 1px 0 #e8c060,   inset -1px -1px 0 #6a4c18,
    inset 4px 4px 0 #ffe9b0,   inset -4px -4px 0 #9a7628,
    inset 8px 8px 0 #c8a24a,   inset -8px -8px 0 #7a5a1c,
    inset 11px 11px 0 #8a6420, inset -11px -11px 0 #5c4214;
}

/* the astrolabe corner - on the corners only, never a second border */
.codex .cnr { position: relative; }
.codex .cnr > i {
  position: absolute; width: 9px; height: 9px; pointer-events: none; z-index: 4;
}
.codex .cnr > i.tl { top: 3px; left: 3px;
  border-top: 1px solid rgba(255,233,176,.55); border-left: 1px solid rgba(255,233,176,.55); }
.codex .cnr > i.tr { top: 3px; right: 3px;
  border-top: 1px solid rgba(255,233,176,.55); border-right: 1px solid rgba(255,233,176,.55); }
.codex .cnr > i.bl { bottom: 3px; left: 3px;
  border-bottom: 1px solid rgba(255,233,176,.55); border-left: 1px solid rgba(255,233,176,.55); }
.codex .cnr > i.br { bottom: 3px; right: 3px;
  border-bottom: 1px solid rgba(255,233,176,.55); border-right: 1px solid rgba(255,233,176,.55); }

/* ---- and the chart's other habit: figures are set in the mono face -----
   The skill chart sets its whole reading card in ui-monospace. Prose in
   this book stays in the game's sans, but every FIGURE - the stat block,
   the register's columns, the tallies, the rung counts - moves to the
   chart's face, which is what those two panels have in common: they are
   both instruments, and an instrument's numbers line up. */
.codex .stats div b,
.codex .reg td,
.codex .reg th,
.codex .tally .big,
.codex .rung .rk,
.codex .lockcost,
.codex .railrow .t s,
.codex .bandhead .fill .n,
.codex .drop .dc,
.codex .drop .dq {
  font-family: ui-monospace, Consolas, "Lucida Console", monospace;
}
.codex .tally .big { letter-spacing: .06em; }
.codex .reg th { letter-spacing: .12em; }


/* The address the player can type, shown only on a server that is not
   answering - see buildServerList. Deliberately quiet: it is the last
   resort on a screen whose whole point is that nobody types an address. */
.sfix { margin-top: 6px; font-size: 11px; text-align: center; }
.sfix a { color: #8a8fa6; text-decoration: underline dotted; }
.sfix a:hover { color: #e8a04a; }

/* ============ THE WAIT AFTER PLAY ==================================
   Between pressing Play and standing in the vale the client is taking the
   map, the monsters, the quests and everyone already logged in. It is not
   long, but it is long enough that a motionless screen reads as a crash -
   and the one thing worse than a wait is a wait that looks like a failure.

   So it says what it is doing, in the game's own voice, and the bar moves.
   The bar is deliberately NOT a progress bar: nothing here knows what
   fraction is done, and a bar that pretends to would sit at 90% and lie.
   It is a barber's pole - it says "still working", which is the only honest
   thing it can say. */
#loading { display: flex; align-items: center; justify-content: center; }
#loading.hidden { display: none; }
.loadbox { width: 420px; max-width: calc(86vw / var(--uiz, 1)); text-align: center; }
.loadbox h2 {
  margin: 0 0 10px; font-size: 22px; letter-spacing: .06em;
  color: #e8c9a0; text-shadow: 0 2px 4px #000, 0 0 14px #e8702c44;
}
#loadnote {
  min-height: 20px; margin: 0 0 14px; font-size: 13px;
  color: #cfc3a8; text-shadow: 0 1px 2px #000, 0 0 6px #000c;
  transition: opacity .25s ease;
}
#loadnote.fade { opacity: 0; }
.loadbar {
  height: 6px; border-radius: 3px; overflow: hidden;
  background: #1a1712; border: 1px solid #3a3128;
}
.loadbar i {
  display: block; height: 100%; width: 40%;
  background: linear-gradient(90deg, #6a3a1a, #e8a04a, #6a3a1a);
  animation: loadslide 1.15s ease-in-out infinite;
}
@keyframes loadslide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
@media (prefers-reduced-motion: reduce) {
  .loadbar i { animation: none; width: 100%; opacity: .5; }
}

/* ---- the mute, on the door ------------------------------------------
   Sits in the corner of the login overlay rather than in the box, so it
   never pushes the title or the server list around. Quiet until you look
   for it, which is right for a control most people will never touch. */
.mutebtn {
  position: absolute; top: 14px; right: 16px; z-index: 4;
  width: 34px; height: 34px; padding: 0; line-height: 1;
  font-size: 15px; cursor: pointer;
  color: #cfc3a8; background: #1a1712cc;
  border: 1px solid #3a3128; border-radius: 6px;
}
.mutebtn:hover { background: #241f18; border-color: #6a5a3a; color: #e8c9a0; }
.mutebtn.off { color: #6f6a5e; }         /* muted: the icon reads as dimmed */

/* ============ THE CRAFTING LIST ====================================
   The kinds are a rail down the left rather than a row of tabs above the
   list: there are seven of them, they wrapped onto two rows, they pushed
   the recipes down the screen, and which one was selected was hard to see.
   A rail is always one column, always the same height, and the selected
   one is unmistakable. */
.craftwrap { display: flex; align-items: stretch; gap: 0; min-height: 0; }
.craftwrap #crafttabs { flex: 0 0 104px; display: flex; flex-direction: column; gap: 2px;
  padding: 6px 6px 6px 0; border-right: 1px solid #2b2937; align-content: flex-start; }
.craftwrap #craftlist { flex: 1; min-width: 0; padding-left: 8px; }
.craftwrap .crafttab { width: 100%; text-align: left; border-left: 2px solid transparent;
  display: flex; justify-content: space-between; align-items: center; }
.craftwrap .crafttab.on { border-left-color: #e8c14a; }
/* a line whenever the bench changes, so a kind reads as sections */
.crafthead { margin: 10px 0 4px; font: 700 10px/1 "Segoe UI", sans-serif; letter-spacing: .14em;
  text-transform: uppercase; color: #7d7a8c; border-bottom: 1px solid #2b2937; padding-bottom: 4px; }
.crafthead:first-child { margin-top: 2px; }
/* what you can make right now, first (2026-10-10) */
.crafthead.ready { color: #7fd08a; border-bottom-color: #2f5a34; }
/* ---- which rung of the ladder a tool is ----
   Filled stars for the rung, hollow for the rest, so "three of five" is
   readable without counting - and the hollow ones are what say there is
   something better to work towards. */
.ctier { font-size: 11px; letter-spacing: 1px; color: #e8c14a; margin-left: 5px; white-space: nowrap; }
.ctier i { font-style: normal; color: #4a4656; }
/* ---- the materials, with their own pictures ----
   A row of names is a row of names; a row of pictures is a shopping list
   you can check against your bag without reading. */
.cmats { display: flex; flex-wrap: wrap; gap: 3px 8px; align-items: center; }
.cmats em { display: inline-flex; align-items: center; gap: 4px; font-style: normal; }
.cmats em img { width: 16px; height: 16px; image-rendering: pixelated; vertical-align: middle;
  background: #17141c; border: 1px solid #2b2937; border-radius: 2px; padding: 1px; }
.cmats em.no img { filter: grayscale(1) brightness(.7); border-color: #4a2a2a; }
/* ============ AND THE PANEL HAS TO BE WIDER FOR IT =================
   Every panel in the game is 420 wide and the crafting one now spends a
   hundred of that on the rail. Four materials with their pictures beside
   them in 300 pixels wraps onto four lines and every row becomes a
   paragraph. This one window gets 580; nothing else moves.

   Bounded by the viewport, because a fixed 580 on a small screen is a
   panel with its buttons off the edge. */
.panel[data-name="crafting"] { width: min(580px, calc(calc(100vw / var(--uiz, 1)) - 96px)); }
.cmats em { font-size: 10.5px; }
.cmats em img { width: 14px; height: 14px; }
/* ---- the crafting level, always on show in the crafting window ---- */
.craftlvl { display: flex; align-items: center; gap: 8px; padding: 2px 0 7px;
  border-bottom: 1px solid #2b2937; margin-bottom: 6px; font-size: 11px; color: #c9c6d6; }
.craftlvl #craftlvltext { font-weight: 600; color: #e8c14a; white-space: nowrap; }
.craftlvl .xpbar { flex: 1; min-width: 40px; }
.craftlvl #craftlvlnext { color: #7d7a8c; white-space: nowrap; }
/* ---- at a bench of your own: your house chests count ----
   One quiet line at the top, and the materials your chests are paying
   towards carry a small warm edge, so "3 + 12 in chests / 4" is read as
   coming from home rather than from the bag. */
.crafthome { margin: -4px 0 8px; padding: 4px 8px; font-size: 11px; color: #e8c9a0;
  background: #2a2016; border: 1px solid #5a4630; border-radius: 3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cmats em.home { color: #e8c9a0; }
.cmats em.home img { border-color: #6a5236; }
/* ============ A TRADE, HAND TO HAND ====================================
   Owner, 2026-09-29: "Items go in each trade window, and have to be
   confirmed by both players before the trade goes off." Two offers side by
   side in the vault's own dark and gold; a side that has accepted is ringed
   green, and what you have offered stays in your bag, dimmed and tagged. */
.panel[data-name="trade"] { width: 560px; }
.tradecols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.tradeside { background: #100f16; border: 1px solid #2b2937; border-radius: 6px; padding: 2px 8px 4px; transition: border-color .2s, box-shadow .2s; }
.tradeside h4.sub { display: flex; justify-content: space-between; align-items: center; color: #c9a227; }
.panel[data-name="trade"] .tradegrid { grid-template-columns: repeat(4, 1fr); }
.tradegrid.droptarget { outline: 1px dashed #e8c14a; outline-offset: 3px; }
.tradegrid .slot.empty { border-style: dashed; cursor: default; opacity: .55; }
.tradeok { font-size: 10px; letter-spacing: 1px; color: #6a6780; text-transform: uppercase; }
.tradeok.yes { color: #7bc86c; font-weight: 700; }
.panel[data-name="trade"].meok .tradeside.mine,
.panel[data-name="trade"].themok .tradeside.theirs { border-color: #7bc86c; box-shadow: 0 0 12px #7bc86c33 inset; }
.tradegold { margin: 7px 0 3px; }
.panel .tradegold .gold { margin: 0; min-width: 34px; }
.tradegold input { padding: 4px 6px; }
.tradegold b { color: #e8c14a; font-size: 13px; }
.tradebtns { margin-top: 10px; }
#btn-tradeok { background: #2c4424; border-color: #5f8a44; color: #e8f4d8; font-weight: 600; padding: 6px 20px; }
#btn-tradeok:hover:not(:disabled) { background: #36542c; border-color: #7bc86c; }
#tradestatus { font-size: 11px; min-height: 16px; color: #c9b46a; }
.slot.offered { opacity: .42; box-shadow: 0 0 0 1px #e8c14a inset; }
.slot .tradepip { position: absolute; left: 1px; bottom: 1px; font-size: 7px; line-height: 9px; letter-spacing: .5px;
  color: #1a1406; background: #e8c14a; border-radius: 2px; padding: 0 2px; font-weight: 700; }

/* ---- friends in the battle list - hover to heal (owner, 2026-09-29) - see refreshBattle ---- */
#battlelist .battlehead { margin: 6px 4px 2px; font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: #8fd09a; opacity: .85; }
#battlelist .battlerow.friend .bn { color: #bdf0c4; }
#battlelist .battlerow.friend .bhp i { background: #5ec86a; }
#battlelist .battlerow.friend .blv.fr { background: #1f3a26; color: #9fe8aa; }
#battlelist .battlerow.friend.party .blv.fr { background: #2a3a5a; color: #a9d0ff; }
#battlelist .battlerow.friend.on { outline: 1px solid #6fdc84; background: #6fdc8418; }

/* ---- the What's new window after an update (owner, 2026-09-29) - see client/js/patchnotes.js ---- */
#patchnotes { position: absolute; left: 50%; top: 45%; transform: translate(-50%, -50%); z-index: 70;
  width: min(520px, calc(calc(100vw / var(--uiz, 1)) - 32px)); max-height: calc(70vh / var(--uiz, 1)); overflow-y: auto; padding: 14px 18px 12px;
  background: #16121fee; border: 1px solid #b5852f; border-radius: 8px;
  box-shadow: 0 0 22px #e8a04a33, 0 8px 34px #000c; color: #e4dcef; font-size: 13px; }
#patchnotes.hidden { display: none; }
#patchnotes .pnhead { font-size: 15px; letter-spacing: .14em; text-transform: uppercase; color: #ffd978; margin-bottom: 6px; }
#patchnotes .pntitle { margin: 8px 0 4px; color: #f0c86a; font-weight: 600; }
#patchnotes .pntitle em { font-style: normal; color: #9a8fae; font-weight: 400; font-size: 11.5px; margin-left: 6px; }
#patchnotes ul { margin: 0 0 4px; padding-left: 18px; }
#patchnotes li { margin: 3px 0; line-height: 1.35; }
#patchnotes li b { font-weight: 600; color: #f1ecf7; }
#patchnotes li span { color: #a99fbd; }
#patchnotes .pnok { display: block; margin: 10px auto 0; min-width: 90px; }
/* the major updates of an entry, as links at the bottom of its card - see MAJOR UPDATES in client/js/patchnotes.js */
#patchnotes .pnmajor { margin: 6px 0 2px; padding: 6px 8px; background: #2a1f0e88; border: 1px solid #b5852f66; border-radius: 5px; color: #d8c8a0; }
#patchnotes .pnmajor span { color: #ffd978; font-weight: 600; margin-right: 4px; }
#patchnotes .pnlink { background: none; border: none; padding: 0 4px; color: #9ad8ff; text-decoration: underline; cursor: pointer; font-size: 13px; }
#patchnotes .pnlink:hover { color: #d8f0ff; }
/* ---- one major update, in a window of its own (owner, 2026-10-05) - the What's new card's look ---- */
#majorwin { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 71;
  width: min(700px, calc(calc(100vw / var(--uiz, 1)) - 32px)); max-height: calc(calc(100vh / var(--uiz, 1)) - 24px); display: flex; flex-direction: column;
  padding: 10px 18px 12px; box-sizing: border-box;
  background: #16121ff4; border: 1px solid #b5852f; border-radius: 8px;
  box-shadow: 0 0 22px #e8a04a33, 0 8px 34px #000c; color: #e4dcef; font-size: 13px; }
#majorwin.hidden { display: none; }
#majorwin .mjhead { display: flex; align-items: center; gap: 8px; padding: 2px 0 8px; font-size: 15px; letter-spacing: .08em;
  text-transform: uppercase; color: #ffd978; flex: none; }
#majorwin .mjhead span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#majorwin .mjx { flex: none; width: 24px; height: 22px; padding: 0; line-height: 18px; font-size: 18px; }
/* the picture at its own pixels - pixel art, so never smoothed when a narrow window shrinks it */
#majorwin .mjpic { flex: none; text-align: center; line-height: 0; }
#majorwin .mjimg { display: inline-block; box-sizing: content-box; width: 560px; max-width: calc(100% - 2px); height: auto; image-rendering: pixelated;
  border: 1px solid #b5852f88; border-radius: 4px; background: #0c0a10; }
#majorwin .mjcap { flex: none; margin: 4px 0 2px; text-align: center; color: #a99fbd; font-size: 11.5px; font-style: italic; }
#majorwin .mjbody { flex: 1 1 auto; min-height: 60px; overflow-y: auto; margin-top: 6px; padding-right: 4px; line-height: 1.38; }
#majorwin .mjbody p { margin: 4px 0; }
#majorwin .mjbody h4 { margin: 8px 0 2px; color: #f0c86a; font-size: 13px; font-weight: 600; }
#majorwin .mjbody ol, #majorwin .mjbody ul { margin: 2px 0 4px; padding-left: 20px; }
#majorwin .mjbody li { margin: 2px 0; }
#majorwin .mjbody ol li::marker { color: #f0c86a; font-weight: 600; }
#majorwin .mjfoot { flex: none; display: flex; align-items: center; justify-content: space-between; margin-top: 8px; }
#majorwin .mjcount { color: #9a8fae; font-size: 12px; }
#majorwin .mjnext { min-width: 90px; }
/* an item's worth, bottom right in gold (owner, 2026-09-29) - see TIP_VALUE in ui.js */
.itemtip .tipval { text-align: right; color: #ffd24a; font-weight: 600; margin-top: 4px; white-space: nowrap; }
/* a set's heading and its tiers - lit when worn, dim until then (owner,
   2026-09-29: "wearing 2, 3 or 5 pieces of the same set") - see shared/sets.js */
.itemtip .tipset { color: #7fe0a0; font-weight: 600; margin-top: 4px; white-space: normal; }
.itemtip .tipset.on { color: #9ff2b4; font-weight: 400; margin-top: 1px; padding-left: 6px; }
.itemtip .tipset.off { color: #6f6a7c; font-weight: 400; margin-top: 1px; padding-left: 6px; }
/* ============ WHAT A FOOD DOES FOR YOU, IN TEAL (owner, 2026-10-01) ====
   "Foods should have their beneficial stats highlighted in teal color, so the
   player knows the difference between them easier." ONE teal, defined here -
   the item tip, the gem window and a meal's buff pill all read it. See
   TIP_GOOD in ui.js; nothing but a food's benefits carries the class. */
:root { --food-good: #3fd6c6; }
.itemtip .tipgood, #gemstats .tipgood, .buffpill .tipgood { color: var(--food-good); }
.buffpill.meal { border-color: #2a8a80; }
/* ============ THE DEATH LOG (owner, 2026-09-29) - see deathLogEl in ui.js ====
   On the death screen under the note, and again in Menu > Last death. The
   killing blow on top, in red; a block or an evade in grey. */
.dlog { width: 520px; max-width: calc(92vw / var(--uiz, 1)); margin: 8px auto 4px; background: #0f0e14e8; border: 1px solid #3a3346;
  border-radius: 6px; padding: 8px 10px; font-size: 11px; color: #cfc8dc; text-align: left; }
.dlog .dlhead { font-size: 12px; margin-bottom: 6px; color: #e6e1ee; }
.dlog .dlhead b { color: #ff8a7a; }
.dlog .dlrows { max-height: 250px; overflow: auto; }
.dlog table { width: 100%; border-collapse: collapse; }
.dlog td { padding: 2px 6px 2px 0; white-space: nowrap; border-bottom: 1px solid #1d1a26; }
.dlog td.dlwho { white-space: normal; }
.dlog td.dlt { color: #8d86a0; width: 42px; }
.dlog td.dldmg { text-align: right; color: #ffb0a0; width: 48px; }
.dlog td.dlhp { text-align: right; color: #9ed79a; width: 70px; }
.dlog tr.dlkill td { background: #5a1c2455; color: #ffd0c8; font-weight: 600; }
.dlog tr.dlmiss td { color: #7d7890; }
.dlog tr.dlmiss td.dldmg { color: #a9c4e8; }
.dlog .dllv { color: #d8b45a; }

/* ============ ROLLED LOOT - see client/js/loot.js =====================
   Owner, 2026-09-29: "drops roll 0-4 random bonus lines with rarity
   colours". Blue for magic, gold for rare, and a sealed rare in gold with
   its ring broken into dashes until Runa has read it. */
.slot.axslot.ax1 { border-color: #6fa8ff; box-shadow: 0 0 0 1px #6fa8ff66 inset, 0 0 6px #6fa8ff33; }
.slot.axslot.ax2 { border-color: #f2c94c; box-shadow: 0 0 0 1px #f2c94c66 inset, 0 0 7px #f2c94c40; }
.slot.axslot.axu { border: 1px dashed #f2c94c; box-shadow: 0 0 7px #f2c94c30; }
.tipax { display: inline; }
.tipax.ax1 { color: #8fbcff; font-weight: 600; }
.tipax.ax2, .tipax.axu { color: #f2c94c; font-weight: 600; }
.tipax.axbase { color: #c8c2d0; }
.tipax.axtag { color: #9a93aa; letter-spacing: 1px; font-size: 10px; }
.tipax.axl { color: #a9c8ff; }
.tipax.axnote { color: #d8b86a; font-style: italic; }
/* UNIDENTIFIED, IN RED (owner 2026-10-11): a sealed piece says so in the
   refusal red - under its name in a tooltip, in front of it in a row */
.tipax.axunid, .axunid { color: #e0453f; font-weight: 700; }
.tipax.axunid { letter-spacing: 1px; }
.axtable { width: 520px; }
.axtable .axhint { margin-bottom: 8px; }
.axwrap { display: flex; gap: 10px; min-height: 180px; }
.axlist { width: 210px; max-height: 260px; overflow: auto; border-right: 1px solid #2a2733; padding-right: 6px; }
.axrow { display: flex; align-items: center; gap: 6px; padding: 3px 4px; border-radius: 4px; cursor: pointer; font-size: 12px; }
.axrow:hover { background: #22202b; }
.axrow.on { background: #2a2636; box-shadow: 0 0 0 1px #4a4458 inset; }
.axrow img { width: 22px; height: 22px; image-rendering: pixelated; }
.axrow i { color: #8a8498; font-style: normal; }
.axnm.ax1, .axbig.ax1 { color: #8fbcff; }
.axnm.ax2, .axbig.ax2, .axnm.axu, .axbig.axu, .shoprow .axu { color: #f2c94c; }
.axview { flex: 1; font-size: 12px; }
.axbig { font-size: 14px; font-weight: 600; color: #e8e2d0; margin-bottom: 2px; }
.axtagline { color: #9a93aa; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; margin: 4px 0 8px; }
.axsealed { color: #d8b86a; font-style: italic; }
.axline { display: flex; align-items: center; gap: 6px; padding: 2px 0; color: #a9c8ff; cursor: pointer; }
.axline.held { color: #ffd24a; }
.axline .axt { color: #6a6478; font-size: 10px; }
.axstones { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; margin-top: 10px; }
.axstone { display: flex; align-items: center; gap: 5px; font-size: 11px; padding: 3px 5px; text-align: left; }
.axstone img { width: 20px; height: 20px; image-rendering: pixelated; }
.axstone b { color: #e8c14a; margin-left: 3px; }
.axstone.on { box-shadow: 0 0 0 1px #e8a04a inset; }
.axword { min-height: 18px; margin-top: 8px; font-size: 12px; color: #e8e2d0; transition: color .6s ease; }
.axword.flash { color: #ffd24a; }
.axline input { width: auto; flex: 0 0 auto; margin: 0; padding: 0; min-width: 0; }
.axline span { flex: 1; text-align: left; }
/* a piece Runa has read opens in the gem window - its tip keeps its lines */
#gemstats:has(.tipax) { white-space: pre-wrap; line-height: 1.45; }
/* ...and its comparison with what you wear lays out as it does in a tip.
   Integration pass, 2026-09-29: with a weapon in hand the read club's
   "Compared with what you have on" ran its cells together ("onRusty
   Shortsword damage6.5+5-1.5swing") - the grid lived under .itemtip only. */
#gemstats .cmp { white-space: normal; margin-top: 7px; padding-top: 6px; border-top: 1px solid #3a3548; }
#gemstats .cmph { font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: #9a93ad; margin-bottom: 5px; }
#gemstats .cmph b { display: block; color: #e8d9a8; letter-spacing: 0; text-transform: none; font-size: 12px; font-weight: 600; }
#gemstats .cmpg { display: grid; grid-template-columns: auto auto auto auto auto; gap: 2px 7px; align-items: baseline; font-variant-numeric: tabular-nums; }
#gemstats .cn { color: #a9a2ba; }
#gemstats .cv { text-align: right; color: #cfc9dc; }
#gemstats .cx { color: #6a6480; }
#gemstats .cd { text-align: right; font-weight: 600; }
#gemstats .up { color: #7fd08a; }
#gemstats .down { color: #ee6a5e; }
#gemstats .same { color: #cfc9dc; }

/* ============ GLOBAL DROPS, THE HALL OF GLORY AND THE CRIER'S RIBBON ======
   Owner, 2026-10-06: "I would like my global drops to be similar to that"
   - client/js/glory.js, behind CONFIG.GLORY2_OPEN. */
/* the line in the System pane, in gold - and louder the rarer it is */
#syslog .line.glory { color: #ffd873; }
#syslog .line.glory-hall { color: #ffe9a8; font-weight: 600; text-shadow: 0 0 6px #e8a04a66; }
#syslog .line.glory-legend { color: #fff3c4; font-weight: 700; background: linear-gradient(90deg, #4a3208cc, #4a320800); border-left: 2px solid #ffd24a; padding-left: 4px; }
#syslog .line.glory-disc { color: #8ff0e0; }

/* THE CRIER'S RIBBON: over the chat, part of it, so it moves when the chat moves */
#gtick {
  position: absolute; left: 0; right: 0; bottom: 100%; margin-bottom: 3px; height: 22px;
  display: flex; align-items: stretch; overflow: hidden;
  background: linear-gradient(180deg, #1d1810e8, #120f0ae8); border: 1px solid #5a4520; border-radius: 4px;
  box-shadow: 0 2px 8px #0008, inset 0 1px 0 #ffd87322;
  font-size: 11px; user-select: none;
}
#gtick.below { bottom: auto; top: 100%; margin: 3px 0 0; }
#gtick.hall { border-color: #c89a3a; box-shadow: 0 2px 8px #0008, 0 0 10px #ffd24a55; }
#gtick.legend { border-color: #ffe9a8; animation: g2burn .7s ease-in-out infinite alternate; }
@keyframes g2burn { from { box-shadow: 0 0 6px #ffd24a88, inset 0 0 6px #ffd24a44; } to { box-shadow: 0 0 18px #ffd24aee, inset 0 0 10px #ffd24a88; } }
#gtick .gt-label {
  flex: none; display: flex; align-items: center; padding: 0 7px; font-size: 9px; letter-spacing: 1.5px; font-weight: 700;
  color: #1a1206; background: linear-gradient(180deg, #ffd873, #d89a2c); border-right: 1px solid #5a4520;
}
#gtick .gt-lane { position: relative; flex: 1; overflow: hidden; }
#gtick .gt-quiet { position: absolute; left: 10px; top: 0; bottom: 0; display: flex; align-items: center; color: #8a7a5c; font-style: italic; }
#gtick .gt-item {
  position: absolute; left: 0; top: 0; bottom: 0; display: flex; align-items: center; gap: 6px; white-space: nowrap;
  padding: 0 10px; color: #ffe08a; text-shadow: 0 1px 2px #000; cursor: pointer; will-change: transform;
  backface-visibility: hidden; transform: translate3d(0, 0, 0);   /* its own layer - see glory.js, no stutter */
}
#gtick .gt-item:hover .gt-txt { text-decoration: underline dotted #ffe08a88; }
#gtick .gt-item b { color: #fff3c4; }
#gtick .gt-item em { font-style: normal; font-weight: 700; color: #ffd24a; }
#gtick .gt-item i { color: #b8a67a; font-style: normal; }
#gtick .gt-badge { font-size: 8px; letter-spacing: 1px; padding: 1px 5px; border-radius: 2px; background: #3a2708; color: #ffd873; border: 1px solid #6a5424; }
#gtick .t-hall .gt-badge { background: linear-gradient(180deg, #ffd873, #c8861c); color: #1a1206; border-color: #ffe9a8; box-shadow: 0 0 8px #ffd24a88; }
#gtick .t-hall em { color: #fff0b0; }
#gtick .t-legend { padding: 0 14px; background: linear-gradient(90deg, #00000000, #5a3a0890 12%, #8a5a10b0 50%, #5a3a0890 88%, #00000000); }
#gtick .t-legend .gt-badge { background: linear-gradient(90deg, #ff5a6e, #ffd040, #7dff8a, #5ad0ff, #ff8ad8); background-size: 300% 100%;
  animation: g2rainbow 1.6s linear infinite; color: #1a0f00; border-color: #fff; font-weight: 800; box-shadow: 0 0 10px #ffd24acc; }
#gtick .t-legend em { color: #ffffff; text-shadow: 0 0 6px #ffd24a, 0 1px 2px #000; }
#gtick .t-disc { color: #bff6ee; }
#gtick .t-disc .gt-badge { background: #0e3a36; color: #8ff0e0; border-color: #2fb0a8; }
#gtick .t-disc em { color: #8ff0e0; }
#gtick .gt-item.me .gt-txt b { color: #ffffff; }
@keyframes g2rainbow { from { background-position: 0% 0; } to { background-position: 300% 0; } }

/* an item's picture, ringed in its rarity */
.g2-ics { display: inline-flex; gap: 3px; align-items: center; flex: none; }
.g2-ic { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px;
  background: #0e0d14; border: 1px solid #3a3446; border-radius: 3px; flex: none; }
.g2-ic.r1 { border-color: #6fa8ff; box-shadow: 0 0 5px #6fa8ff66; }
.g2-ic.r2 { border-color: #f2c94c; box-shadow: 0 0 6px #f2c94c88; }
.g2-ic img { width: 80%; height: 80%; image-rendering: pixelated; }
.g2-ic i { position: absolute; right: 0; bottom: -1px; font-size: 9px; font-style: normal; color: #f0e2b0; text-shadow: 0 1px 1px #000, 0 0 2px #000; }
#gtick .g2-ic { width: 16px; height: 16px; }

/* "gz" */
.g2-gz { position: fixed; z-index: 400; background: #1a1610f4; border: 1px solid #8a6a28; border-radius: 6px; padding: 7px 9px;
  box-shadow: 0 8px 24px #000c; font-size: 11px; color: #e8dcc0; display: flex; flex-direction: column; gap: 6px; align-items: center; }
.g2-gz-h { color: #ffd873; }
.g2-gz-b { background: linear-gradient(180deg, #5a4214, #3a2a0c); border-color: #c89a3a; color: #fff0c0; font-weight: 600; padding: 4px 12px; }
.g2-gz-b:hover { background: linear-gradient(180deg, #6a5018, #4a360e); border-color: #ffd24a; }
.g2-click { cursor: pointer; }

/* THE HALL */
.panel[data-name="glory"].g2 { width: 640px; max-width: calc(calc(100vw / var(--uiz, 1)) - 24px); max-height: calc(calc(100vh / var(--uiz, 1)) - 96px); padding: 10px 12px 12px;
  background: linear-gradient(180deg, #19150fee, #14131bee 160px); border-color: #5a4520; }
.panel[data-name="glory"].g2 h3 { color: #ffd24a; letter-spacing: 1px; }
.g2-hero { position: relative; overflow: hidden; border: 1px solid #8a6a28; border-radius: 6px; padding: 8px 12px 10px; margin-bottom: 8px;
  background: radial-gradient(ellipse at 20% 0%, #5a3f12 0%, #2a1e0c 55%, #17120c 100%); box-shadow: inset 0 0 22px #00000080, 0 0 14px #e8a04a22; }
.g2-hero::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 35%, #fff3c420 48%, transparent 60%); background-size: 260% 100%; animation: g2shine 4.5s ease-in-out infinite; }
@keyframes g2shine { 0% { background-position: 130% 0; } 60%, 100% { background-position: -130% 0; } }
.g2-hero.t-legend { border-color: #ffd24a; box-shadow: inset 0 0 22px #00000080, 0 0 18px #ffd24a44; }
.g2-hero-k { font-size: 9px; letter-spacing: 2px; color: #d8b060; font-weight: 700; margin-bottom: 6px; }
.g2-hero-none { color: #b8a67a; font-style: italic; padding: 8px 0 4px; }
.g2-hero-body { display: flex; align-items: center; gap: 12px; position: relative; z-index: 1; }
.g2-hero-body .g2-ics { gap: 5px; }
.g2-hero-mid { flex: 1; min-width: 0; }
.g2-hero-name { font-size: 18px; font-weight: 800; color: #fff3c4; text-shadow: 0 2px 0 #000, 0 0 10px #e8a04a55; }
.g2-hero-sub { font-size: 11px; color: #e0cc98; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-hero-sub.dim { color: #9a8a64; }
.g2-hero-val { text-align: right; flex: none; line-height: 1; }
.g2-hero-val .num { display: block; font-size: 30px; font-weight: 900; color: #ffd24a; letter-spacing: 1px; font-variant-numeric: tabular-nums;
  text-shadow: 0 2px 0 #6a3a00, 0 3px 0 #2a1600, 0 0 16px #ffb02088; }
.g2-hero-val .unit { font-size: 10px; letter-spacing: 3px; color: #d8b060; text-transform: uppercase; }
.g2-ctl { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.g2-ctl .spantabs { margin: 2px 0 6px; }
.g2-ctl .spantabs.cat .spantab.on { background: #18282a; border-color: #4fd8c8; color: #bff6ee; }
.g2-chips { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; margin-bottom: 8px; }
.g2-chip { background: #15131c; border: 1px solid #2e2a3a; border-radius: 4px; padding: 4px 6px; min-width: 0; }
.g2-chip .k { display: block; font-size: 8.5px; letter-spacing: .8px; text-transform: uppercase; color: #8b8898; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-chip b { display: block; font-size: 12px; color: #e8e6ef; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-chip.gold b, .g2-chip.hall b { color: #ffd873; }
.g2-chip.me { border-color: #4a6a3a; }
.g2-chip.me b { color: #b8f0a0; }
.g2-h { margin: 6px 0 5px; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #c8a050; }
.g2-podium { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: 6px; align-items: end; margin-bottom: 6px; }
.g2-pod { position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px; text-align: center;
  padding: 6px 4px 0; border-radius: 5px 5px 0 0; min-width: 0; }
.g2-pod.p1 { background: linear-gradient(180deg, #3a2c0e, #1d160a); border: 1px solid #c89a3a; border-bottom: none; }
.g2-pod.p2 { background: linear-gradient(180deg, #262a32, #16181d); border: 1px solid #8a94a4; border-bottom: none; }
.g2-pod.p3 { background: linear-gradient(180deg, #2e2014, #19120c); border: 1px solid #9a6a3a; border-bottom: none; }
.g2-pod.empty { opacity: .45; }
.g2-medal { width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 900; font-size: 12px;
  color: #1a1206; box-shadow: 0 2px 0 #0008, inset 0 1px 0 #fff8; }
.p1 .g2-medal { background: radial-gradient(circle at 35% 30%, #fff3c4, #f2c94c 45%, #b07a10); width: 26px; height: 26px; font-size: 14px; }
.p2 .g2-medal { background: radial-gradient(circle at 35% 30%, #ffffff, #c9d1dc 45%, #7a8494); }
.p3 .g2-medal { background: radial-gradient(circle at 35% 30%, #ffe0c0, #d08a4a 45%, #8a4a1a); }
.g2-pod-name { font-weight: 700; color: #fff3c4; font-size: 12px; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.p1 .g2-pod-name { font-size: 14px; }
.g2-pod-val { color: #ffd24a; font-weight: 800; font-size: 14px; font-variant-numeric: tabular-nums; }
.p1 .g2-pod-val { font-size: 18px; text-shadow: 0 0 10px #ffb02066; }
.g2-pod-val small { font-size: 9px; color: #c8a050; font-weight: 600; }
.g2-pod-sub { font-size: 10px; color: #9a8a64; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-pod .g2-badge { position: absolute; top: 5px; right: 5px; }
.g2-step { align-self: stretch; margin: 4px -5px 0; }
.p1 .g2-step { height: 26px; background: linear-gradient(180deg, #c89a3a, #7a5418); }
.p2 .g2-step { height: 16px; background: linear-gradient(180deg, #8a94a4, #4a5260); }
.p3 .g2-step { height: 10px; background: linear-gradient(180deg, #9a6a3a, #5a3a1a); }
.g2-badge { font-size: 8px; letter-spacing: 1px; font-weight: 800; padding: 1px 5px; border-radius: 2px; flex: none; }
.g2-badge.hall { background: linear-gradient(180deg, #ffd873, #c8861c); color: #1a1206; }
.g2-badge.legend { background: linear-gradient(90deg, #ff5a6e, #ffd040, #7dff8a, #5ad0ff, #ff8ad8); background-size: 300% 100%; animation: g2rainbow 1.6s linear infinite; color: #1a0f00; }
.g2-badge.disc { background: #0e3a36; color: #8ff0e0; border: 1px solid #2fb0a8; }
.g2-badge.plain { background: #2a2210; color: #c8a050; }
.g2-badge.none { visibility: hidden; }
.g2-rows { display: flex; flex-direction: column; gap: 3px; margin-bottom: 4px; }
.g2-row { display: flex; align-items: center; gap: 7px; padding: 3px 6px; background: #15131c; border: 1px solid #26232f; border-radius: 4px; font-size: 11px; }
.g2-row:hover, .g2-frow:hover, .g2-disc:hover { border-color: #6a5424; background: #1c1810; }
.g2-row.me { border-color: #4a6a3a; }
.g2-row .rk { width: 16px; text-align: center; color: #8b8898; font-weight: 800; }
.g2-row .who { width: 150px; min-width: 0; display: flex; flex-direction: column; line-height: 1.15; }
.g2-row .who b { color: #e8e6ef; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-row .who i { color: #8b8898; font-style: normal; font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-row .bar { position: relative; flex: 1; height: 14px; background: #0e0d14; border-radius: 2px; overflow: hidden; }
.g2-row .bar i { position: absolute; left: 0; top: 0; bottom: 0; background: linear-gradient(90deg, #7a5418, #e8a04a); border-radius: 2px; }
.g2-row .bar em { position: absolute; right: 5px; top: 0; line-height: 14px; font-style: normal; font-weight: 800; color: #fff3c4; text-shadow: 0 1px 1px #000; font-variant-numeric: tabular-nums; }
.g2-row .ago { width: 48px; text-align: right; color: #6d6a7a; font-size: 10px; }
.g2-two { display: flex; gap: 10px; }
.g2-col { flex: 1.6; min-width: 0; }
.g2-col.narrow { flex: 1; }
.g2-feed { display: flex; flex-direction: column; gap: 2px; }
.g2-frow { display: flex; align-items: center; gap: 6px; padding: 2px 5px; border: 1px solid transparent; border-radius: 3px; font-size: 11px; }
.g2-frow .ago { width: 46px; color: #6d6a7a; font-size: 10px; flex: none; }
.g2-frow .txt { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #c9c6d6; }
.g2-frow .txt b { color: #e8e6ef; }
.g2-frow .txt em { color: #8ff0e0; font-style: normal; }
.g2-frow .val { color: #ffd24a; font-weight: 700; font-variant-numeric: tabular-nums; }
.g2-frow.t-legend .txt b, .g2-frow.t-hall .txt b { color: #fff3c4; }
.g2-crit { display: flex; flex-direction: column; gap: 3px; }
.g2-critc { display: grid; grid-template-columns: 1fr auto; gap: 0 6px; padding: 3px 6px; background: #15131c; border: 1px solid #26232f; border-radius: 4px; font-size: 11px; }
.g2-critc b { color: #e8e6ef; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-critc .n { color: #ffd873; font-weight: 800; }
.g2-critc .best { grid-column: 1 / 3; color: #8b8898; font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-discs { display: flex; flex-direction: column; gap: 4px; }
.g2-disc { display: flex; align-items: center; gap: 9px; padding: 5px 8px; background: #121a1a; border: 1px solid #1f3a38; border-radius: 4px; }
.g2-disc .mid { flex: 1; min-width: 0; }
.g2-disc .nm { color: #8ff0e0; font-weight: 700; font-size: 13px; }
.g2-disc .sub { color: #8b9898; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.g2-disc .sub b { color: #e8e6ef; }
.g2-disc .when { color: #6d7a7a; font-size: 10px; }
.g2-foot { margin: 8px 0 0; font-size: 10px; }

/* ============ THE SORT MODE, BESIDE THE SORT BUTTON =====================
   Owner, 2026-10-15 - shared/bagsort.js, client/js/ui.js THE SORT MODE.
   One fixed width that holds the longest of the six labels, so picking a
   mode never moves the button, the gold or the window. The menu is the
   right-click menu's own look (.itemmenu). */
#btn-sortmode.sortmode {
  width: 116px; flex: 0 0 116px; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: #c9c4d8; padding: 6px 7px; font-size: 11px;
}
/* the gold and the rift shards drop under the buttons when a big purse will
   not fit beside them - the buttons themselves never move */
.panel .row.sortrow { flex-wrap: wrap; row-gap: 2px; }
.panel .row.sortrow #btn-sortinv { flex: 0 0 auto; }
#btn-sortmode.sortmode.open { border-color: #e8a04a; color: #e8e6ef; }
.itemmenu.sortmenu { min-width: 128px; }
.itemmenu.sortmenu button.on { color: #e8a04a; }
.itemmenu.sortmenu button.on::before { content: '\2713  '; }
.itemmenu.sortmenu button:not(.on) { padding-left: 20px; }

/* ============ THE PLAYTEST GATE (owner, 2026-10-17) =====================
   "PLAYTEST IN 03:09" under the title while the server holds the door shut
   - client/js/launchgate.js. Gold like the title, square and blocky like
   its pixels, on a dark plate so it reads on any part of the painting. */
.launchgate {
  margin: 0 auto 4px; padding: 10px 22px 10px; text-align: center; pointer-events: none;
  background: #120e0ad9; border: 2px solid #8a6326; border-radius: 3px;
  box-shadow: 0 0 0 2px #000a, 0 6px 26px #000c, inset 0 0 18px #e8702c22;
  font-family: ui-monospace, Consolas, 'Courier New', monospace; color: #f2c55a;
  text-shadow: 0 2px 0 #5a3a10, 0 0 14px #e8702c66, 0 0 4px #000;
}
.launchgate.hidden { display: none; }
.launchgate .lg-head { font-size: 15px; font-weight: 700; letter-spacing: .32em; color: #ffd98a; }
.launchgate .lg-clock { display: flex; justify-content: center; align-items: flex-start; gap: 6px; margin-top: 2px; }
.launchgate .lg-clock.hidden, .launchgate .lg-soon.hidden, .launchgate .lg-admin.hidden { display: none; }
.launchgate .lg-part { display: flex; flex-direction: column; align-items: center; min-width: 74px; }
.launchgate .lg-part b { font-size: 52px; line-height: 1; font-weight: 800; letter-spacing: .04em; }
.launchgate .lg-colon { font-size: 46px; line-height: 1; font-weight: 800; }
.launchgate .lg-part small {
  font-size: 10px; letter-spacing: .28em; text-transform: uppercase; color: #d9b98a;
  text-shadow: 0 1px 2px #000; margin-top: 2px;
}
.launchgate .lg-soon { font-size: 26px; font-weight: 800; letter-spacing: .12em; margin: 6px 0 2px; }
.launchgate .lg-line {
  margin-top: 6px; font-family: "Segoe UI", system-ui, sans-serif; font-size: 13px; color: #f3e3c3;
  letter-spacing: .02em; text-shadow: 0 1px 2px #000;
}
.launchgate .lg-admin { margin-top: 3px; font-size: 11px; color: #9be29b; letter-spacing: .06em; text-shadow: 0 1px 2px #000; }
/* on the character select it hangs under the title, over the scene */
#login.charsel .launchgate {
  position: fixed; z-index: 7; top: calc(18px + min(300px, calc(26vw / var(--uiz, 1))) * .42);
  left: var(--cs-cx, 50%); transform: translateX(-50%);
}
