/* style.css -- the 1999 EverQuest stone chrome (card NORRSTONE1999, restyled to the owner's
   reference screenshots of the original full-screen interface): light gray marbled stone
   plates, chiseled bevels, engraved serif lettering, round spell gems, segmented vital bars,
   and a parchment scroll for the chat. The whole interface is opaque panels pinned to the
   edges of the screen, because that is what a fixed 640x480 layout grew into.

   Card NORRUICOLORS: the chrome colours are NOT written here. They are authored on z.norrath.ui
   and js/ui/chrome.js writes them onto custom properties on the document element at load; this
   file reads them back through var():

     --bar-health  <- ui.barHealth    --window-fill    <- ui.windowFill
     --bar-mana    <- ui.barMana      --window-frame   <- ui.windowFrame
     --bar-stamina <- ui.barStamina   --window-text    <- ui.windowText
     --bar-exp     <- ui.barExp       --parchment-fill <- ui.parchmentFill
                                      --parchment-text <- ui.parchmentText

   None of them has a var() fallback, deliberately: a fallback is a second copy of the authored
   value, and a second copy going stale is the exact bug that card removed. Every other tone in
   this file is DERIVED from those authored colours with color-mix(), so the palette still has
   exactly one home. */

* { box-sizing: border-box; }

/* The two faces of the interface, bundled in assets/fonts (OFL -- see LICENSES.md there):
   Cinzel carves the buttons and plaques the way the era's incised Roman faces did, and
   EB Garamond writes the book hand on parchment. Loaded from the repo so file:// play and a
   dead network change nothing. */
@font-face {
  font-family: 'Cinzel';
  src: url('assets/fonts/cinzel-latin-400-normal.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Cinzel';
  src: url('assets/fonts/cinzel-latin-700-normal.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'EB Garamond';
  src: url('assets/fonts/eb-garamond-latin-400-normal.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'EB Garamond';
  src: url('assets/fonts/eb-garamond-latin-500-normal.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}

/* The stone material, derived once and read everywhere. --stone-grain is an inline SVG noise
   tile blended over the authored fill so the surface reads as cut rock rather than flat paint;
   it carries only neutral gray, never a copy of an authored colour. */
:root {
  --stone-lite: color-mix(in srgb, var(--window-fill) 55%, white);
  --stone-dark: color-mix(in srgb, var(--window-fill) 55%, black);
  --stone-line: color-mix(in srgb, var(--window-frame) 65%, black);
  --stone-recess: color-mix(in srgb, var(--window-fill) 32%, black);
  --stone-hover: color-mix(in srgb, var(--window-fill) 82%, white);
  --stone-sel: color-mix(in srgb, var(--window-fill) 58%, white);
  --stone-face: color-mix(in srgb, var(--window-fill) 86%, white);
  --ink: var(--window-text);
  --ink-soft: color-mix(in srgb, var(--window-text) 72%, var(--window-fill));
  --ink-dim: color-mix(in srgb, var(--window-text) 52%, var(--window-fill));
  --ink-strong: color-mix(in srgb, var(--window-text) 85%, black);
  --recess-text: color-mix(in srgb, var(--parchment-fill) 88%, white);
  --etch: rgba(255, 255, 255, 0.38);
  --emboss-hi: rgba(255, 255, 255, 0.55);
  --emboss-lo: rgba(0, 0, 0, 0.55);
  --font-carved: 'Cinzel', Georgia, 'Times New Roman', serif;
  --font-book: 'EB Garamond', Georgia, 'Times New Roman', serif;
  --stone-accent: color-mix(in srgb, var(--bar-stamina) 55%, var(--window-fill));
  --parchment-line: color-mix(in srgb, var(--parchment-text) 45%, var(--parchment-fill));
  --stone-grain: url('data:image/svg+xml;utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%22160%22 height=%22160%22><filter id=%22m%22><feTurbulence type=%22fractalNoise%22 baseFrequency=%220.055%22 numOctaves=%223%22 seed=%227%22 stitchTiles=%22stitch%22/><feColorMatrix type=%22matrix%22 values=%220 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.52  0 0 0 0.5 0%22/></filter><filter id=%22g%22><feTurbulence type=%22fractalNoise%22 baseFrequency=%220.8%22 numOctaves=%222%22 seed=%2211%22 stitchTiles=%22stitch%22/><feColorMatrix type=%22matrix%22 values=%220 0 0 0 0.45  0 0 0 0 0.45  0 0 0 0 0.47  0 0 0 0.3 0%22/></filter><rect width=%22160%22 height=%22160%22 filter=%22url(%23m)%22/><rect width=%22160%22 height=%22160%22 filter=%22url(%23g)%22/></svg>');
}

html, body {
  margin: 0; padding: 0; height: 100%; overflow: hidden;
  background: #000;
  color: var(--window-text);
  font-family: var(--font-book);
  font-size: 13px;
  user-select: none;
  -webkit-user-select: none;
}

#screen { position: fixed; inset: 0; }
#gl, #overlay { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* The world takes an ordinary cursor. Mouselook hides it via pointer lock, not via CSS, so the
   cursor is always real and always clickable when the right button is not held. */
#gl { cursor: default; }
#overlay { pointer-events: none; }

#crosshair {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  color: rgba(240, 230, 200, 0.55); font-size: 18px; pointer-events: none; display: none;
  text-shadow: 0 0 3px #000;
}
#crosshair.on { display: block; }

/* ---------- window chrome ---------- */
/* Every window is a slab of the same marbled stone: the authored ui.windowFill under the grain
   tile, a dark outline, and a chiseled bevel -- light along the top and left where the quarry
   light falls, dark along the bottom and right. Text on stone is engraved: dark ink with a thin
   light lip beneath it. */
.eqwin {
  background-color: var(--window-fill);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line);
  border-radius: 2px;
  box-shadow:
    inset 2px 2px 0 var(--emboss-hi),
    inset -2px -2px 0 var(--emboss-lo),
    inset 0 0 22px rgba(0, 0, 0, 0.16),
    0 2px 8px rgba(0, 0, 0, 0.6);
  padding: 4px 6px;
  color: var(--window-text);
  text-shadow: 0 1px 0 var(--etch);
}
.wintitle {
  font-family: var(--font-carved);
  font-weight: bold; color: var(--ink-strong); letter-spacing: 1px;
  font-variant: small-caps;
  border-bottom: 1px solid var(--stone-dark); margin: -1px -2px 4px; padding-bottom: 3px;
  box-shadow: 0 1px 0 var(--etch);
  text-shadow: 0 1px 0 var(--etch);
}
.closex {
  float: right; cursor: pointer; color: var(--ink-soft); padding: 0 4px; font-weight: bold;
}
.closex:hover { color: var(--ink-strong); }
.hidden { display: none !important; }

/* A button is a raised plate of the same stone; pressing it (or a toggle held on) sinks it. */
button {
  font-family: var(--font-carved); font-size: 11px; font-weight: bold; letter-spacing: 1px;
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line); color: var(--ink);
  padding: 3px 10px; cursor: pointer; border-radius: 2px;
  box-shadow:
    inset 2px 2px 0 var(--emboss-hi),
    inset -2px -2px 0 var(--emboss-lo),
    0 1px 2px rgba(0, 0, 0, 0.5);
  text-shadow: 0 1px 0 var(--etch);
}
button:hover { background-color: var(--stone-hover); color: var(--ink-strong); }
button:active {
  background-color: var(--stone-sel);
  box-shadow:
    inset -1px -1px 0 var(--etch),
    inset 1px 1px 3px rgba(0, 0, 0, 0.55);
  transform: translate(1px, 1px);
}
button:disabled { opacity: 0.4; cursor: default; }

/* Every slider wears the stone's own accent rather than the browser's blue. */
input[type="range"] { accent-color: var(--stone-accent); }

input {
  font-family: inherit; font-size: 12px; width: 100%;
  background: var(--stone-recess); border: 1px solid var(--stone-line);
  color: var(--recess-text); padding: 3px 5px;
  box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.6);
  text-shadow: none;
}

/* ---------- hud layout ---------- */
#hud { position: absolute; inset: 0; pointer-events: none; }
#hud .eqwin { pointer-events: auto; }

#playerWin { position: absolute; left: 8px; top: 8px; width: 208px; }
/* Sits below the help strip, which also occupies the top centre. */
#targetWin { position: absolute; left: 50%; top: 28px; transform: translateX(-50%); width: 240px; }
/* Sits below the menu button, which owns the top-right corner. */
#gemBar    { position: absolute; right: 8px; top: 40px; width: 78px; }
#chatWin   { position: absolute; left: 8px; bottom: 8px; width: 480px; height: 190px; padding: 4px; }
#compass   { position: absolute; right: 8px; bottom: 8px; width: 132px; text-align: center; }
/* Card NORRCLASSICUI: the stone block -- paged hot buttons and the Main buttons stacked on
   the right edge, the surviving core of the 1999 full-screen frame. The pager row spans the
   grid; sockets sit two across, five down; cooldown/toggle state renders the button held
   pressed IN (owner-recall tier, flagged unverified in MECHANICS_SOURCES.md section 23). */
#hotbar    { position: absolute; right: 8px; top: 110px; display: grid;
             grid-template-columns: repeat(2, 80px); gap: 3px; padding: 6px; }
#hotbar .hotslot { min-height: 24px; font-size: 10px; overflow: hidden;
                   text-overflow: ellipsis; white-space: nowrap; text-align: center;
                   line-height: 24px; padding: 0 3px; box-sizing: border-box; }
.hotpager  { grid-column: 1 / -1; display: flex; justify-content: space-between;
             align-items: center; color: var(--ink-soft); font-size: 10px; font-weight: bold; }
.hotpager .pgarrow { cursor: pointer; padding: 0 8px; user-select: none; }
.hotpager .pgarrow:hover { color: var(--ink-strong); }
.hotslot.empty { min-height: 24px; }
.assignmenu { position: absolute; z-index: 90; width: 160px; padding: 4px; }
.assignmenu .assigntitle { color: var(--ink-soft); font-size: 10px; padding: 2px 4px 4px; }
.assignmenu .assignrow { padding: 3px 6px; cursor: pointer; }
.assignmenu .assignrow:hover { background: rgba(255, 240, 200, 0.12); }
.assignmenu .assignrow.clear { color: #b88; }
.hotslot.on { transform: translate(1px, 1px);
              box-shadow: inset 1px 1px 4px rgba(0, 0, 0, 0.75); }
#mainButtons { position: absolute; right: 8px; top: 342px; width: 143px; padding: 6px; }
#mainButtons .mainbtn { display: block; margin: 3px 0; padding: 4px 6px; text-align: center;
                        cursor: pointer; }
#mainButtons .mainbtn.on { transform: translate(1px, 1px);
                           box-shadow: inset 1px 1px 4px rgba(0, 0, 0, 0.75); }
body.touchmode #mainButtons { display: none; }

/* Cards NORRSTONEFRAME / NORRSTONE1999: the classic frame -- the world set into the upper
   left, fixed stone panels owning the LEFT column, the RIGHT column and the BOTTOM band, F10
   flipping back to the floating layout. The split sizes ride CSS variables written from the
   same authored DB.ui numbers the renderer subtracts, so the drawn viewport and these panels
   always agree. The window pins use !important deliberately: a dragged window carries inline
   coordinates, and while the frame is on the frame must win; drag positions come back
   untouched the moment F10 lifts it.

   The panels are quarried plates: the shared stone material plus horizontal masonry seams (a
   dark cut with a lit lip under it), and a chiseled edge where each panel meets the world. */
#stoneLeft, #stoneRight, #stoneBottom { display: none; }
body.stonemode #stoneLeft,
body.stonemode #stoneRight,
body.stonemode #stoneBottom {
  display: block; position: fixed; z-index: 5;
  background-color: var(--window-fill);
  background-image:
    repeating-linear-gradient(180deg,
      transparent 0, transparent 91px,
      rgba(0, 0, 0, 0.34) 91px, rgba(0, 0, 0, 0.34) 93px,
      var(--etch) 93px, var(--etch) 94px),
    var(--stone-grain);
  box-shadow:
    inset 2px 2px 0 var(--emboss-hi),
    inset -2px -2px 0 var(--emboss-lo),
    inset 0 0 34px rgba(0, 0, 0, 0.18);
}
body.stonemode #stoneLeft {
  top: 0; left: 0; width: var(--stone-left-w); height: 100vh;
  border-right: 2px solid var(--stone-line);
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px 56px 10px 10px;
}
body.stonemode #stoneRight {
  top: 0; right: 0; width: var(--stone-right-w); height: 100vh;
  border-left: 2px solid var(--stone-line);
}
body.stonemode #stoneBottom {
  left: var(--stone-left-w); bottom: 0; height: var(--stone-bottom-h);
  width: calc(100vw - var(--stone-left-w) - var(--stone-right-w));
  border-top: 2px solid var(--stone-line);
  background-image:
    repeating-linear-gradient(90deg,
      transparent 0, transparent 179px,
      rgba(0, 0, 0, 0.34) 179px, rgba(0, 0, 0, 0.34) 181px,
      var(--etch) 181px, var(--etch) 182px),
    var(--stone-grain);
}

/* The left column's carved furniture: the Help / Options / Persona stack, the plaque, and
   Spells / View, in the reference's own order. The 56px right padding above keeps the flow
   clear of the spell gem strip pinned along the column's inner edge. */
.stonebtn {
  flex: none;
  font-family: var(--font-carved);
  font-weight: bold; font-size: 12px; letter-spacing: 1px; font-variant: small-caps;
  text-align: center; cursor: pointer; padding: 5px 4px; border-radius: 2px;
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line); color: var(--ink);
  box-shadow:
    inset 2px 2px 0 var(--emboss-hi),
    inset -2px -2px 0 var(--emboss-lo),
    0 1px 2px rgba(0, 0, 0, 0.5);
  text-shadow: 0 1px 0 var(--etch);
}
.stonebtn:hover { background-color: var(--stone-hover); color: var(--ink-strong); }
.stonebtn:active {
  background-color: var(--stone-sel);
  box-shadow: inset -1px -1px 0 var(--etch), inset 1px 1px 3px rgba(0, 0, 0, 0.55);
  transform: translate(1px, 1px);
}
#stonePlaque {
  flex: none; height: 150px; margin: 2px 0; border-radius: 2px;
  background-color: var(--stone-recess);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line);
  box-shadow: inset 3px 3px 8px rgba(0, 0, 0, 0.7), inset -1px -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 var(--emboss-hi);
}

body.stonemode #playerWin {
  left: calc(100vw - var(--stone-right-w) + 10px) !important; top: 8px !important;
  right: auto !important; width: calc(var(--stone-right-w) - 26px) !important;
  z-index: 10;
}
body.stonemode #targetWin {
  left: calc(100vw - var(--stone-right-w) + 10px) !important; top: 192px !important;
  transform: none !important; width: calc(var(--stone-right-w) - 26px) !important;
  z-index: 10;
}
/* The paged hot buttons live in the left column's lower half, the reference's own place for
   them, above whatever bottom edge the screen has. */
body.stonemode #hotbar {
  left: 10px !important; right: auto !important; top: auto !important; bottom: 12px !important;
  width: calc(var(--stone-left-w) - 68px);
  grid-template-columns: repeat(2, 1fr);
  z-index: 10;
}
/* The carved face is wider than the old monospace, so the sockets give their labels the whole
   cell instead of a fixed 34 pixels. */
body.stonemode #hotbar .hotslot { width: auto; min-height: 30px; font-size: 10px; }
/* The Main block: wide engraved rectangles stacked in the right column, under the target. */
body.stonemode #mainButtons {
  right: 10px !important; left: auto !important; top: 300px !important;
  width: calc(var(--stone-right-w) - 20px);
  z-index: 10;
}
body.stonemode #compass {
  right: 10px !important; bottom: 8px !important; left: auto !important; z-index: 10;
}
/* The chat parchment unrolls across the bottom band, between the two columns. */
body.stonemode #chatWin {
  left: calc(var(--stone-left-w) + 10px) !important;
  top: calc(100vh - var(--stone-bottom-h) + 8px) !important;
  bottom: auto !important;
  width: calc(100vw - var(--stone-left-w) - var(--stone-right-w) - 20px) !important;
  height: calc(var(--stone-bottom-h) - 16px) !important; z-index: 10;
}
/* The spell gems hang as a strip of round sockets along the left column's inner edge. The
   window dressing comes off: no plate, no title, no numbers -- just the gems, like the
   reference. hud.js already hides the whole bar for a character with no spells. */
body.stonemode #gemBar {
  left: calc(var(--stone-left-w) - 50px) !important; top: 88px !important;
  right: auto !important; width: 42px; padding: 2px 0; z-index: 10;
  background: none; border: none; box-shadow: none;
}
body.stonemode #gemBar .wintitle { display: none; }
body.stonemode #gemBar .gemnum { display: none; }
body.stonemode #gemBar .gem { justify-content: center; padding: 2px 0; margin: 5px 0; }
body.stonemode #gemBar .gem:hover { background: none; }
/* The crosshair centres on the drawn viewport, which sits inset from the panels. */
body.stonemode #crosshair {
  left: calc(var(--stone-left-w) + (100vw - var(--stone-left-w) - var(--stone-right-w)) / 2);
  top: calc((100vh - var(--stone-bottom-h)) / 2);
}
body.touchmode #stoneLeft, body.touchmode #stoneRight, body.touchmode #stoneBottom { display: none; }

/* bars -- the segmented gauges of the reference: a dark recess cut into the stone, the
   authored fill colour inside it, and cell notches painted over the top by the ::after so the
   fill reads as a row of lit blocks. */
.barrow { display: flex; align-items: center; gap: 4px; margin: 2px 0; }
.barlabel { width: 18px; color: var(--ink-soft); font-size: 10px; }
.bar {
  position: relative; flex: 1; height: 13px;
  background: var(--stone-recess); border: 1px solid var(--stone-line); overflow: hidden;
  box-shadow: inset 1px 1px 2px rgba(0, 0, 0, 0.65), 0 1px 0 var(--etch);
}
.bar::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.28) 0 3px, transparent 3px 9px, rgba(0, 0, 0, 0.3) 10px),
    repeating-linear-gradient(90deg, transparent 0 7px, rgba(0, 0, 0, 0.38) 7px 9px);
}
.fill { height: 100%; width: 0%; transition: width 0.12s linear; }
.bartext {
  position: absolute; inset: 0; text-align: center; line-height: 11px;
  font-size: 10px; text-shadow: 0 0 2px #000, 0 0 2px #000; color: #f0e8d0;
  z-index: 1;
}
/* Four of the seven fills are the authored ui.bar* colours, flat, for the same reason .eqwin's
   fill is flat: each bar used to be a two-stop gradient whose stops were nowhere in the data (the
   authored colour sits between them), and there is no authored light/dark pair to rebuild one
   from. The other three are still hardcoded because nothing is authored for them at all: breath
   and the cast bar have no field on z.norrath.ui, and neither does the bevel shading. */
#hpBar { background: var(--bar-health); }
#manaBar { background: var(--bar-mana); }
#stamBar { background: var(--bar-stamina); }
#xpBar { background: var(--bar-exp); }
#breathBar { background: linear-gradient(#40c8e0, #1878a0); }
#tHpBar { background: var(--bar-health); }
#castFill { background: linear-gradient(#c8a0f0, #7040b0); }

#pStats, #tInfo {
  font-size: 10px; color: var(--ink-soft); margin-top: 3px; line-height: 1.5;
  border-top: 1px solid var(--stone-dark); padding-top: 3px;
}
#buffRow { display: flex; flex-wrap: wrap; gap: 2px; margin-top: 3px; }
.buffchip {
  font-size: 9px; padding: 1px 4px; border: 1px solid var(--stone-line);
  background: var(--stone-face); color: var(--ink-soft);
}

/* target */
#targetWin.con-grey  .wintitle { color: #8a8a8a; }
#targetWin.con-green .wintitle { color: #4fbf4f; }
#targetWin.con-lightblue .wintitle { color: #7fd8ff; }
#targetWin.con-blue  .wintitle { color: #5b7fe0; }
#targetWin.con-white .wintitle { color: #f0f0f0; }
#targetWin.con-yellow .wintitle { color: #e8d24a; }
#targetWin.con-red   .wintitle { color: #e04040; }

/* cast bar */
#castBar {
  position: absolute; left: 50%; bottom: 220px; transform: translateX(-50%);
  width: 260px; text-align: center;
  background: rgba(10, 8, 4, 0.9); border: 2px solid var(--window-frame); padding: 3px 6px;
}
#castName { font-size: 11px; color: #c8a0f0; margin-bottom: 2px; }

/* spell gems -- round polished stones set into carved sockets, the reference's left-edge
   strip. The socket ring is the stone itself; the gem inside keeps its spell colour. */
.gem {
  display: flex; align-items: center; gap: 4px; margin: 3px 0;
  cursor: pointer; padding: 1px 2px;
}
.gem:hover { background: var(--stone-hover); }
.gemorb {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--stone-line); flex: none;
  box-shadow:
    inset -2px -2px 4px rgba(0, 0, 0, 0.7), inset 2px 2px 4px rgba(255, 255, 255, 0.35),
    0 1px 0 var(--etch), 0 0 0 1px rgba(0, 0, 0, 0.35);
}
.gemnum { font-size: 9px; color: var(--ink-dim); width: 8px; }
.gem.cooling .gemorb { filter: grayscale(1) brightness(0.45); }
.gem.empty .gemorb { box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.8), 0 1px 0 var(--etch); }

/* hotbar -- each hot button is a small raised stone plate with an engraved lowercase label,
   matching the reference's assist / atk / backof grid. A held toggle sinks into its socket. */
.hotslot {
  width: 34px; height: 34px; border: 1px solid var(--stone-line);
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  font-family: var(--font-carved);
  font-size: 9px; color: var(--ink); display: flex; align-items: center; justify-content: center;
  text-align: center; cursor: pointer; line-height: 1.05; padding: 1px;
  box-shadow:
    inset 2px 2px 0 var(--emboss-hi),
    inset -2px -2px 0 var(--emboss-lo),
    0 1px 2px rgba(0, 0, 0, 0.5);
  text-shadow: 0 1px 0 var(--etch);
}
.hotslot:hover { background-color: var(--stone-hover); color: var(--ink-strong); }
.hotslot.on {
  border-color: var(--stone-line); color: var(--ink-strong);
  background-color: var(--stone-sel);
}
.hotslot.empty {
  background-color: var(--stone-recess); background-image: var(--stone-grain);
  box-shadow: inset 2px 2px 5px rgba(0, 0, 0, 0.6), 0 1px 0 var(--etch);
}

/* chat -- the parchment scroll of the reference: the authored parchment tone under a light
   grain, rolled edges suggested by the darkened curls at either end, and the authored
   channel inks written by js/ui/chat.js on each line. */
#chatWin.eqwin {
  background-color: var(--parchment-fill);
  background-image:
    linear-gradient(90deg, rgba(60, 40, 10, 0.4), transparent 14px, transparent calc(100% - 14px), rgba(60, 40, 10, 0.4)),
    linear-gradient(180deg, rgba(60, 40, 10, 0.22), transparent 8px, transparent calc(100% - 8px), rgba(60, 40, 10, 0.22)),
    var(--stone-grain);
  border: 2px solid var(--parchment-line);
  border-radius: 6px;
  box-shadow:
    inset 0 0 30px rgba(60, 40, 10, 0.3),
    0 2px 8px rgba(0, 0, 0, 0.6);
  color: var(--parchment-text);
  text-shadow: none;
}
#chatLog {
  height: calc(100% - 4px); overflow-y: auto; padding: 0 8px 0 10px;
  font-size: 12px; line-height: 1.35; word-wrap: break-word;
  color: var(--parchment-text);
}
#chatLog::-webkit-scrollbar { width: 9px; }
#chatLog::-webkit-scrollbar-track { background: color-mix(in srgb, var(--parchment-fill) 80%, black); }
#chatLog::-webkit-scrollbar-thumb {
  background: var(--stone-face); border: 1px solid var(--stone-line);
}
.chatline { margin: 0; }
/* The command line only appears while it has focus, so it never eats chat space. */
#chatInput {
  margin-top: 3px;
  background: color-mix(in srgb, var(--parchment-fill) 86%, white);
  border: 1px solid var(--parchment-line); color: var(--parchment-text);
  font-size: 12px; padding: 2px 4px; box-shadow: none;
}
#chatWin:has(#chatInput:not(.hidden)) #chatLog { height: calc(100% - 26px); }

/* compass -- an engraved dial plate at the foot of the right column. */
#compassHeading { font-size: 22px; font-weight: bold; color: var(--ink-strong); line-height: 1.1; }
#compassPlace { font-size: 10px; color: var(--ink-soft); min-height: 12px; }
#clockText { font-size: 10px; color: var(--ink-dim); }
#musicToggle {
  font-size: 10px; color: var(--ink-dim); cursor: pointer; margin-top: 2px;
  border-top: 1px solid var(--stone-dark); padding-top: 3px;
}
#musicToggle:hover { color: var(--ink-strong); }
#musicToggle.off { color: var(--ink-dim); opacity: 0.7; }

/* ---------- popups ---------- */
.popup { position: absolute; z-index: 20; }
#invWin { right: 96px; top: 8px; width: 330px; }
#invBody { display: flex; gap: 8px; }
#equipCol, #packCol { flex: 1; }
.slotrow, .itemrow {
  display: flex; justify-content: space-between; gap: 6px;
  padding: 2px 3px; font-size: 11px; cursor: pointer; border: 1px solid transparent;
}
.slotrow:hover, .itemrow:hover { background: var(--stone-hover); border-color: #57482a; }
.slotname { color: var(--ink-dim); text-transform: capitalize; }
.itemname { color: var(--window-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.itemname.magic { color: #8fd8ff; }
.itemqty { color: var(--ink-dim); flex: none; }
.emptyslot { color: var(--ink-dim); font-style: italic; }
#coinRow, #merchantCoin {
  border-top: 1px solid var(--stone-line); margin-top: 5px; padding-top: 4px;
  font-size: 11px; color: var(--ink-strong);
}

#lootWin { left: 50%; top: 40%; transform: translate(-50%, -50%); width: 260px; }
#lootBody { max-height: 220px; overflow-y: auto; }
.winbuttons { margin-top: 6px; text-align: right; }

#merchantWin { left: 50%; top: 50%; transform: translate(-50%, -50%); width: 520px; }
#merchantBody { display: flex; gap: 10px; }
.mcol { flex: 1; }
#stockList, #sellList { height: 260px; overflow-y: auto; }
.price { color: #d8b860; flex: none; font-size: 10px; }

#bankWin { left: 50%; top: 50%; transform: translate(-50%, -50%); width: 300px; }
#bankBody { line-height: 2; }

#bookWin { left: 50%; top: 50%; transform: translate(-50%, -50%); width: 420px; }
#bookBody { max-height: 320px; overflow-y: auto; }
.spellrow {
  display: flex; align-items: center; gap: 6px; padding: 3px; cursor: pointer;
  border: 1px solid transparent;
}
.spellrow:hover { background: var(--stone-hover); border-color: #57482a; }
.spelldot { width: 12px; height: 12px; border-radius: 50%; border: 1px solid var(--stone-line); flex: none; }
.spellmeta { color: var(--ink-dim); font-size: 10px; margin-left: auto; }

#mapWin { left: 50%; top: 50%; transform: translate(-50%, -50%);
         width: 420px; height: 440px; display: flex; flex-direction: column; }
#mapCanvas { display: block; background: var(--stone-recess); border: 1px solid var(--stone-line);
             flex: 1; width: 100%; min-height: 0; touch-action: none; cursor: grab; }

.fieldlabel {
  font-family: var(--font-carved);
  color: var(--ink-soft); font-size: 10px; text-transform: uppercase; letter-spacing: 1px;
  margin: 8px 0 3px;
}

/* ---------- menu button and menu ----------
   z-index 90 puts the button above every window, the dev panel and the death screen. The one
   affordance that always works must never end up behind something you are trying to dismiss. */
#menuButton {
  position: absolute; right: 8px; top: 8px; z-index: 90;
  width: 26px; height: 26px; line-height: 24px; text-align: center;
  font-size: 15px; cursor: pointer; user-select: none;
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line); border-radius: 2px; color: var(--ink);
  box-shadow:
    inset 2px 2px 0 var(--emboss-hi), inset -2px -2px 0 var(--emboss-lo),
    0 2px 8px rgba(0, 0, 0, 0.6);
}
#menuButton:hover { background-color: var(--stone-hover); color: var(--ink-strong); }
#menuButton.on { background-color: var(--stone-sel); color: var(--ink-strong); }

#mainMenu { position: absolute; right: 8px; top: 40px; z-index: 89; width: 150px; }
#menuItems { display: flex; flex-direction: column; gap: 4px; }
#menuItems button { width: 100%; text-align: left; padding: 5px 10px; font-size: 12px; }

#optionsPanel { position: absolute; right: 166px; top: 40px; z-index: 89; width: 250px; }
#optionsBody { padding-top: 2px; }
.optrow { margin-bottom: 9px; }
.optlabel { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink-soft); }
.optlabel b { color: var(--ink-strong); font-weight: normal; }
.optrow input[type="range"] { width: 100%; margin-top: 2px; accent-color: var(--stone-accent); }
.optrow label { font-size: 11px; cursor: pointer; display: flex; align-items: center; gap: 6px; }
.optgroup { border-top: 1px solid var(--stone-line); }
.optgroup:first-child { border-top: none; }
.optgrouphead {
  cursor: pointer; padding: 5px 2px; color: var(--ink-soft); font-size: 11px; letter-spacing: 0.5px;
  font-weight: bold;
}
.optgrouphead:hover { color: var(--ink-strong); }
.optcaret { color: var(--ink-dim); display: inline-block; width: 10px; }
.optgroupbody { padding: 2px 2px 6px 12px; }
.optsub {
  color: var(--ink-dim); font-size: 10px; text-transform: uppercase; letter-spacing: 1px;
  margin: 4px 0 5px;
}
.optchoices { display: flex; gap: 3px; margin-top: 3px; flex-wrap: wrap; }
.optchoice { font-size: 10px; padding: 2px 8px; }
.optchoice.on { background: var(--stone-sel); border-color: #d8c070; color: var(--ink-strong); }
.optnote { color: var(--ink-dim); font-size: 10px; line-height: 1.5; margin-bottom: 7px; }
.optbtns { border-top: 1px solid var(--stone-line); padding-top: 7px; text-align: right; }

/* ---------- dev tools ----------
   Intentionally NOT dressed as game UI. Flat grey, system font, no gold: a screenshot should
   never leave any doubt about which panel ships and which one is scaffolding. */
#devMenu {
  position: absolute; left: 8px; top: 8px; z-index: 45; width: 340px; max-width: 92vw;
  background: rgba(16, 18, 22, 0.96);
  border: 1px solid #4a5560; border-radius: 3px;
  font-family: Consolas, "Courier New", monospace; font-size: 11px; color: #c8d2da;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.8);
}
#devHead {
  background: #222932; border-bottom: 1px solid #4a5560; padding: 4px 8px;
  font-weight: bold; letter-spacing: 1px; color: #8fd0ff;
}
#devHead .devhint { color: #5f6b76; font-weight: normal; letter-spacing: 0; margin-left: 6px; }
#devClose { float: right; cursor: pointer; color: #8fa0ae; padding: 0 3px; }
#devClose:hover { color: #fff; }
#devBody { padding: 6px 8px 8px; max-height: 74vh; overflow-y: auto; }
.devsection + .devsection { margin-top: 10px; border-top: 1px solid #333c45; padding-top: 8px; }
.devtitle { color: #7f8c98; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 4px; }
.devrow { display: flex; justify-content: space-between; gap: 10px; line-height: 1.65; }
.devrow span { color: #7f8c98; }
.devrow b { color: #e2eaf0; font-weight: normal; }
.devbtns { margin-top: 6px; color: #7f8c98; display: flex; flex-wrap: wrap; gap: 3px; align-items: center; }
#devMenu button {
  font-family: inherit; font-size: 10px; background: #2b333c; border: 1px solid #4a5560;
  color: #c8d2da; padding: 2px 7px; border-radius: 2px; cursor: pointer;
}
#devMenu button:hover { background: #38424d; color: #fff; }
#devMenu button.on { background: #1d5a80; border-color: #8fd0ff; color: #fff; }
#devScrub { width: 100%; margin: 8px 0 2px; accent-color: #8fd0ff; }
.devcheck {
  display: flex; align-items: center; gap: 6px; margin-top: 8px;
  cursor: pointer; color: #c8d2da;
}
.devcheck input { width: auto; accent-color: #8fd0ff; }
.devslider { display: grid; grid-template-columns: 92px 44px 1fr; gap: 6px; align-items: center; margin-top: 3px; }
.devslider span { color: #7f8c98; }
.devslider b { color: #e2eaf0; font-weight: normal; text-align: right; }
.devslider input { width: 100%; accent-color: #8fd0ff; }
.devnote { margin-top: 7px; color: #6f7c88; line-height: 1.5; }

/* ---------- death ---------- */
#deathScreen {
  position: absolute; inset: 0; z-index: 40;
  background: rgba(30, 0, 0, 0.72);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
#deathText { font-size: 34px; color: #ff6060; letter-spacing: 3px; text-shadow: 0 0 18px #500; }
#deathSub { margin-top: 14px; color: #d8b0b0; }

/* ---------- loading ---------- */
#loadingScreen {
  position: absolute; inset: 0; z-index: 50; background: #000;
  display: flex; align-items: center; justify-content: center;
}
#loadingText { color: var(--ink-soft); letter-spacing: 4px; font-size: 15px; }

/* ---------- title ----------
   The cursor is an ordinary cursor on every menu screen; only mouselook hides it. */
#titleScreen {
  position: absolute; inset: 0; z-index: 70; background: #050806;
  display: flex; align-items: center; justify-content: center;
}
#titleCanvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
#titleInner { position: relative; text-align: center; padding: 0 20px; }
#titleWord {
  font-size: clamp(38px, 9vw, 96px); letter-spacing: clamp(6px, 2.2vw, 22px);
  font-weight: bold; color: #f0dfa8;
  text-shadow: 0 0 26px rgba(200, 240, 150, 0.28), 0 4px 0 #241a08, 0 6px 18px rgba(0, 0, 0, 0.9);
}
#titleRule {
  height: 2px; margin: 12px auto 14px; max-width: 460px;
  background: linear-gradient(90deg, transparent, var(--window-frame) 20%, #d8c070 50%, var(--window-frame) 80%, transparent);
}
#titleSub {
  color: #b9c9a4; font-size: clamp(11px, 1.5vw, 14px); letter-spacing: 2px;
  text-shadow: 0 2px 6px #000;
}
#titleButtons { margin-top: 34px; }
#titleEnterBtn {
  font-size: 15px; padding: 10px 34px; letter-spacing: 2px;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.7);
}
#titleAudioNote {
  margin-top: 14px; min-height: 14px; font-size: 10px; letter-spacing: 1px;
  color: rgba(200, 220, 170, 0.55); text-shadow: 0 2px 6px #000;
}
#titleMuteBtn {
  margin-top: 10px; font-size: 22px; cursor: pointer; opacity: 0.55;
  transition: opacity 0.15s;
}
#titleMuteBtn:hover { opacity: 0.85; }
#titleMuteBtn.muted { opacity: 0.3; }
#titleFoot {
  margin-top: 26px; color: rgba(160, 175, 140, 0.65); font-size: 10px; letter-spacing: 3px;
  text-shadow: 0 2px 6px #000;
}
#titleBuild {
  margin-top: 8px; color: rgba(160, 175, 140, 0.35); font: 9px monospace; letter-spacing: 1px;
}

/* ---------- character select ---------- */
#selectScreen {
  position: absolute; inset: 0; z-index: 65;
  background-color: var(--window-fill);
  background-image:
    radial-gradient(ellipse at 50% 42%, transparent 45%, rgba(0, 0, 0, 0.42)),
    repeating-linear-gradient(180deg,
      transparent 0, transparent 115px,
      rgba(0, 0, 0, 0.3) 115px, rgba(0, 0, 0, 0.3) 117px,
      var(--etch) 117px, var(--etch) 118px),
    repeating-linear-gradient(90deg,
      transparent 0, transparent 235px,
      rgba(0, 0, 0, 0.22) 235px, rgba(0, 0, 0, 0.22) 237px,
      var(--etch) 237px, var(--etch) 238px),
    var(--stone-grain);
  display: flex; align-items: center; justify-content: center;
}
#selectBox { width: min(760px, 92vw); }
#charList {
  border: 1px solid var(--stone-line); background: var(--stone-recess);
  min-height: 210px; max-height: 46vh; overflow-y: auto;
}
.charrow {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  padding: 7px 10px; cursor: pointer; border-bottom: 1px solid var(--stone-dark);
}
.charrow:hover { background: color-mix(in srgb, var(--stone-recess) 70%, white); }
.charrow.sel { background: var(--stone-sel); }
.charname { font-size: 14px; color: var(--recess-text); font-weight: bold; }
.charrow.sel .charname { color: var(--ink-strong); }
.charmeta { grid-column: 1; color: color-mix(in srgb, var(--recess-text) 72%, transparent); font-size: 11px; }
.charwhere { grid-column: 2; grid-row: 1 / span 2; align-self: center; color: color-mix(in srgb, var(--recess-text) 60%, transparent); font-size: 11px; }
.emptyroster { padding: 26px; text-align: center; color: var(--recess-text); opacity: 0.6; font-style: italic; }
#selectHint { margin-top: 6px; font-size: 10px; color: var(--ink-dim); }
#selectButtons { margin-top: 10px; display: flex; gap: 6px; justify-content: flex-end; }
#deleteCharBtn.confirm { border-color: #b04040; color: #ff9090; }

/* ---------- character creation ---------- */
#createScreen {
  position: absolute; inset: 0; z-index: 60;
  background-color: var(--window-fill);
  background-image:
    radial-gradient(ellipse at 50% 42%, transparent 45%, rgba(0, 0, 0, 0.42)),
    repeating-linear-gradient(180deg,
      transparent 0, transparent 115px,
      rgba(0, 0, 0, 0.3) 115px, rgba(0, 0, 0, 0.3) 117px,
      var(--etch) 117px, var(--etch) 118px),
    repeating-linear-gradient(90deg,
      transparent 0, transparent 235px,
      rgba(0, 0, 0, 0.22) 235px, rgba(0, 0, 0, 0.22) 237px,
      var(--etch) 237px, var(--etch) 238px),
    var(--stone-grain);
  display: flex; align-items: center; justify-content: center;
}
#createBox {
  width: min(1280px, 95vw); max-height: 94vh;
  display: flex; flex-direction: column; overflow: hidden;
}
#createBody { overflow-y: auto; flex: 1; min-height: 0; padding-right: 5px; }
#createBody::-webkit-scrollbar { width: 10px; }
#createBody::-webkit-scrollbar-track {
  background: var(--stone-recess); border: 1px solid var(--stone-line);
}
#createBody::-webkit-scrollbar-thumb {
  background: var(--stone-face); border: 1px solid var(--stone-line);
  box-shadow: inset 1px 1px 0 var(--etch);
}
#createBody { display: flex; gap: 14px; }
.createcol { flex: 1; min-width: 0; }
.createcol.wide { flex: 1.5; }
.picklist { max-height: 250px; overflow-y: auto; border: 1px solid var(--stone-line); background: var(--stone-recess); }
.picklist.short { max-height: 130px; }
.pick { padding: 3px 6px; cursor: pointer; font-size: 11px; }
.pick:hover { background: var(--stone-hover); }
.pick.sel { background: var(--stone-sel); color: var(--ink-strong); }
.pick.off { color: var(--ink-dim); cursor: default; }
.pick.off:hover { background: transparent; }
#statBlock { border: 1px solid var(--stone-line); background: var(--stone-recess); padding: 4px 6px; }
.statrow { display: flex; align-items: center; gap: 6px; padding: 2px 0; }
.statname { width: 34px; color: var(--ink-soft); }
.statval { width: 34px; text-align: right; color: var(--ink-strong); }
.statbtn {
  width: 18px; height: 16px; line-height: 1; padding: 0; font-size: 11px;
}
/* The paperdoll column. EverQuest's creation screen let you change your head and nothing else,
   so that is what these controls are: a face, hair, colour, beard, and the model to see them on. */
.createcol.doll { flex: 0 0 232px; }
#createDoll {
  width: 220px; height: 320px; display: block;
  border: 1px solid var(--stone-line); background: #0d1108; image-rendering: pixelated;
  cursor: ew-resize;
}
#createDollNote { font-size: 10px; color: var(--ink-dim); margin: 3px 0 6px; text-align: center; }
.headpick { display: flex; align-items: center; gap: 4px; margin-bottom: 3px; }
.headpick .hplabel { width: 46px; font-size: 10px; color: var(--ink-soft); }
.headpick .hpname {
  flex: 1; min-width: 0; text-align: center; font-size: 10px; color: var(--ink-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.headpick button { width: 18px; height: 16px; line-height: 1; padding: 0; font-size: 11px; }
.headpick.off { opacity: 0.35; }
.swatchrow { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 2px; }
.swatch {
  width: 15px; height: 15px; border: 1px solid var(--stone-line); cursor: pointer; padding: 0;
}
.swatch.on { border-color: var(--ink-strong); box-shadow: 0 0 0 1px #ffe6a0 inset; }

/* The same figure again in the inventory, where the point is watching a piece go on. */
#invDoll {
  width: 132px; height: 192px; display: block; margin: 0 auto 4px;
  border: 1px solid var(--stone-line); background: #0d1108; image-rendering: pixelated;
  cursor: ew-resize;
}

.namerow { display: flex; gap: 4px; }
.namerow input { flex: 1; min-width: 0; }
#getNameBtn { flex: 0 0 auto; padding: 3px 8px; white-space: nowrap; }

#createDesc {
  border: 1px solid var(--parchment-line); background: var(--parchment-fill); padding: 6px;
  min-height: 92px; font-size: 11px; line-height: 1.5; color: var(--parchment-text);
}
#pointsLeft { color: var(--ink-strong); }
#createError { color: #ff8f8f; font-size: 11px; min-height: 14px; margin-top: 8px; }
#createStart {
  margin-top: 8px; padding-top: 8px; flex: none;
  display: flex; gap: 6px; justify-content: flex-end;
  border-top: 1px solid var(--stone-dark); box-shadow: 0 -1px 0 var(--etch);
}
#startBtn { font-size: 13px; padding: 6px 18px; }

/* ---------- narrow viewports ----------
   The layout targets a wide window, but the panes this runs in are often much narrower. Below
   these widths the fixed-width windows would overlap each other and run off the screen, so the
   chat log and the popups shrink and the help strip gets out of the way. */
@media (max-width: 1100px) {
  #chatWin { width: 42vw; min-width: 260px; height: 150px; }
  #invWin { width: 300px; right: 92px; }
  #merchantWin { width: 92vw; }
  #bookWin { width: 92vw; }
  #createBox { width: 96vw; }
  #createBody { flex-wrap: wrap; }
  .createcol { min-width: 200px; }
  .createcol.doll { flex: 0 0 232px; }
}

@media (max-width: 780px) {
  body { font-size: 11px; }
  #targetWin { top: 8px; width: 200px; }
  #playerWin { width: 172px; }
  #chatWin { left: 4px; right: 4px; width: auto; height: 132px; bottom: 4px; }
  /* The hotbar and compass would land on top of the chat log, so they move above it. */
  #hotbar { bottom: 142px; }
  #compass { bottom: 142px; right: 4px; width: 104px; }
  #castBar { bottom: 186px; width: 200px; }
  #gemBar { right: 4px; top: 36px; width: 62px; }
  #invWin, #lootWin, #merchantWin, #bankWin, #bookWin, #mapWin {
    left: 50%; right: auto; top: 46%; transform: translate(-50%, -50%);
    width: 94vw; max-height: 70vh; overflow: auto;
  }
  #merchantBody { flex-direction: column; }
  #stockList, #sellList { height: 150px; }
  #mapCanvas { flex: 1; width: 100%; min-height: 0; }
  #mainMenu { right: 4px; top: 36px; width: 132px; }
  #optionsPanel { right: 4px; top: 76px; width: calc(100vw - 8px); max-width: 250px; }
}

/* ===================== movable windows ===================== */

/* Card NORRCENTERED: windows with no authored screen anchor used to land at the top-left
   corner. They open centered instead; the moment the player drags one, the window system
   writes an inline position and clears the transform, and the remembered spot wins forever
   after (js/ui/windowframe.js drag + apply both set transform to none). */
#questWin, #factionWin, #bagWin, #examineWin, #audioWin, #charWin, #keysWin, #tradeWin {
  left: 50%; top: 42%; transform: translate(-50%, -50%);
}
#optionsPanel {
  left: 50%; top: 42%; right: auto; transform: translate(-50%, -50%);
  /* The panel is a drawer chest, not a wall: it never exceeds the screen, and the body
     scrolls inside it with a visible stone thumb. */
  display: flex; flex-direction: column;
  max-height: 84vh; width: 270px;
}
#optionsBody {
  overflow-y: auto; flex: 1; min-height: 0; padding-right: 5px;
  scrollbar-gutter: stable;
}
#optionsBody::-webkit-scrollbar { width: 10px; }
#optionsBody::-webkit-scrollbar-track {
  background: var(--stone-recess); border: 1px solid var(--stone-line);
}
#optionsBody::-webkit-scrollbar-thumb {
  background: var(--stone-face); border: 1px solid var(--stone-line);
  box-shadow: inset 1px 1px 0 var(--etch);
}
/* Each group head is a drawer front: a raised stone strip that plainly invites the click. */
.optgrouphead {
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line); border-radius: 2px;
  margin: 3px 0; padding: 5px 6px;
  box-shadow: inset 2px 2px 0 var(--emboss-hi), inset -2px -2px 0 var(--emboss-lo);
  font-family: var(--font-carved); font-variant: small-caps; letter-spacing: 1px; color: var(--ink);
}
.optgrouphead:hover { background-color: var(--stone-hover); }
/* On touch the whole sheet is the scroller (the sheet rule below sets its own overflow), so
   the body hands scrolling up rather than nesting a second one inside it. Both faces of the
   interface -- desktop stone and touch sheet -- carry the same drawers and the same order. */
body.touchmode #optionsBody { overflow: visible; padding-right: 0; }

/* Card NORRDEVWIN: the dev menu is a window like any other now -- dragged by its head,
   resized by its corner; the body scrolls inside whatever size it is given. */
#devMenu { display: flex; flex-direction: column; }
#devMenu.hidden { display: none; }
#devBody { overflow-y: auto; flex: 1; min-height: 0; }
/* Every popup can be dragged by its title bar. The bar keeps its ordinary look; only the cursor
   says it is grabbable, because a 1999 window had no visible handle either. */
.draghandle { cursor: move; }
.draghandle:active { cursor: grabbing; }

/* The grip sits in the bottom-right corner as three short diagonal rules, the way every resizable
   window of the era drew it. It is drawn by the window's own ::after rather than by an appended
   element: hud.js rebuilds the gem bar and the action bar with innerHTML, and an appended child
   does not survive that. windowframe.js reads the press position instead of the press target, so
   the drawing and the hit test are independent of the window's contents. */
.resizable::after {
  content: "";
  position: absolute; right: 1px; bottom: 1px; width: 14px; height: 14px;
  cursor: nwse-resize; opacity: 0.55;
  background:
    linear-gradient(135deg, transparent 45%, var(--stone-dark) 45%, var(--stone-dark) 55%, transparent 55%) 0 0/6px 6px,
    linear-gradient(135deg, transparent 45%, var(--stone-dark) 45%, var(--stone-dark) 55%, transparent 55%) 4px 4px/6px 6px;
  background-repeat: no-repeat;
}
.resizable:hover::after { opacity: 0.9; }

/* ===================== examine ===================== */
#examineWin { width: 260px; }
#examineBody { max-height: 340px; overflow-y: auto; padding: 2px 4px 6px; }
.exname { color: var(--ink-strong); font-weight: bold; margin-bottom: 3px; }
.exkind { color: var(--ink-soft); margin-bottom: 5px; }
.exrow { display: flex; justify-content: space-between; gap: 8px; line-height: 1.45; }
.exrow span:last-child { color: var(--ink-soft); }
.exflags { margin-top: 5px; color: #2f6e2f; }
.exlore { margin-top: 6px; padding-top: 5px; border-top: 1px solid var(--stone-line);
  color: #b0a480; font-style: italic; line-height: 1.4; }

/* ===================== drag and drop ===================== */
/* The row you picked up dims but stays in place, so the pack does not reflow under the cursor. */
.dragging { opacity: 0.35; }
/* A slot that will accept what you are holding lights its edge. */
.dropready { outline: 1px solid var(--ink-strong); outline-offset: -1px; background: var(--stone-sel); }

/* ===================== journal ===================== */
#questWin { width: 320px; }
#questBody { max-height: 400px; overflow-y: auto; padding: 2px 4px 6px; }
.questrow { padding: 4px 2px; border-bottom: 1px solid var(--stone-dark); }
.questrow:last-child { border-bottom: none; }
.questname { color: var(--ink-strong); }
.questgiver { color: var(--ink-soft); }
.questneed { color: var(--ink-soft); padding-left: 10px; }
.questready { color: #2f6e2f; }
.questdone { color: #7d8a6a; }
.questdone .questname { color: #9aa88a; }

/* ===================== faction ===================== */
#factionWin { width: 300px; }
#factionBody { max-height: 400px; overflow-y: auto; padding: 4px 4px 6px; }
.facrow { margin-bottom: 7px; }
.facname { color: var(--window-text); }
.facword { color: var(--ink-soft); float: right; }
/* The bar is centred: neutral is the middle, and standing grows out from it either way. */
.facbar {
  position: relative; height: 8px; margin-top: 2px;
  background: var(--stone-recess); border: 1px solid var(--stone-line);
}
.facbar::before {
  content: ''; position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: #6b5a2e;
}
.facfill { position: absolute; top: 0; bottom: 0; }
.facfill.good { background: #4e8a3e; }
.facfill.bad { background: #8a3e3e; }
.faczero { color: var(--ink-dim); }

/* A task you can hand in right now is worth spotting from across the window. */
.questrow.ready { background: color-mix(in srgb, var(--stone-sel) 70%, green); }
.questneed.met { color: #2f6e2f; }

/* The audio register: a scrolling list of what is sounding right now. */
.devlist { max-height: 180px; overflow-y: auto; margin: 2px 0 4px; }
.devlist .devrow span { color: #b0a880; }

/* ===================== the audio listener ===================== */
/* Its own window. The controls sit ABOVE the list, because the list changes several times a
   second and anything under it would be a moving target. */
#audioWin { width: 300px; }
#audioTools { text-align: left; border: 0; padding: 0 2px 4px; }
#audioTools button { font-size: 11px; padding: 2px 5px; }
#audioCount { color: var(--ink-soft); padding: 0 4px 3px; border-bottom: 1px solid var(--stone-line); }
#audioList { max-height: 320px; overflow-y: auto; padding: 3px 4px 4px; }
/* Rows are appended and never reordered, so nothing jumps while you are reading it. */
.audiorow {
  display: grid; grid-template-columns: 1fr auto; gap: 0 8px;
  line-height: 1.4; transition: opacity 120ms linear;
}
.audioname { color: var(--window-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.audiometa { color: var(--ink-soft); }
.audiobar {
  grid-column: 1 / -1; height: 2px; background: var(--stone-recess); margin-bottom: 3px; display: block;
}
.audiobar i { display: block; height: 100%; background: var(--stone-accent); }
/* A finished row stays put and fades, rather than vanishing and pulling the list up. */
.audiorow.done .audioname { color: var(--ink-dim); }
.audiorow.done .audiobar i { background: var(--stone-dark); }

/* ===================== icons ===================== */
/* Drawn at 32px and shown smaller, so the edges stay clean on any display. Never draggable in
   their own right: the ROW is the drag handle, and a browser will happily drag a bare image out
   from under one. */
.icon {
  width: 18px; height: 18px; flex: 0 0 18px;
  image-rendering: -webkit-optimize-contrast;
  vertical-align: middle;
  -webkit-user-drag: none; user-select: none;
}
/* An empty equipment slot keeps the space, so the column does not jump when you take something
   off and put it back on. */
.iconempty {
  display: inline-block;
  border: 1px dashed var(--stone-dark); opacity: 0.5; box-sizing: border-box;
}
.iconbig { width: 40px; height: 40px; flex: 0 0 40px; }

/* Rows lay out as icon, name, then whatever is on the right. */
.itemrow, .slotrow, .spellrow { display: flex; align-items: center; gap: 5px; }
.itemrow .itemname, .slotrow .itemname, .spellrow span:not(.spellmeta) { flex: 1 1 auto; }

/* The examine window puts the picture beside the name rather than above it. */
.exhead { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.exhead .exname { margin-bottom: 0; }

/* Spell gems: the picture IS the gem now. */
.gemicon { width: 22px; height: 22px; flex: 0 0 22px; }
.gem.cooling .gemicon { opacity: 0.35; filter: grayscale(0.7); }

/* Spellbook rows. */
.bookicon { width: 16px; height: 16px; flex: 0 0 16px; }

/* ===================== buffs and debuffs ===================== */
/* A chip says what is on you, whether it is helping, and how long is left. The border colour is
   the spell's own; the tint behind it is what separates a blessing from a problem. */
.buffchip {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 4px 1px 2px; margin: 1px 2px 1px 0;
  border: 1px solid var(--stone-line); border-radius: 2px;
  font-size: 10px; line-height: 1.4;
}
.buffchip.good { background: rgba(60, 90, 50, 0.35); }
.buffchip.bad { background: rgba(110, 45, 40, 0.4); }
.bufficon { width: 14px; height: 14px; flex: 0 0 14px; }
.bufflabel { color: var(--window-text); }
.buffticks { color: var(--ink-soft); }
.buffchip.bad .buffticks { color: #e0a090; }

/* ===================== character sheet ===================== */
#charWin { width: 300px; }
#charBody { max-height: 460px; overflow-y: auto; padding: 2px 5px 6px; }
.charhead { color: var(--ink-strong); font-size: 14px; }
.charsub { color: var(--ink-soft); margin-bottom: 5px; }
.charnote { color: var(--ink-soft); font-style: italic; margin: 2px 0 4px; }

/* A labelled bar, used for every condition on the sheet. */
.charmeter { display: grid; grid-template-columns: 62px 1fr 34px; gap: 6px; align-items: center;
  line-height: 1.6; }
.charbar { height: 7px; background: var(--stone-recess); border: 1px solid var(--stone-line); display: block; }
.charbar i { display: block; height: 100%; background: #6b8a4a; }
.charbar i.hp { background: #9a3b34; }
.charbar i.mana { background: #4a63a8; }
.charbar i.stam { background: #b08a3a; }
.charbar i.food { background: #8a6a3a; }
.charbar i.water { background: #4a86a8; }
.charmeter b { color: var(--ink-soft); text-align: right; }

.charstats { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; }
.charstat { display: flex; justify-content: space-between; line-height: 1.5; }
.charstat span { color: var(--ink-soft); }
.charstat b { color: var(--window-text); }

/* Skills, ranked highest first: the answer to "what am I good at". */
.skillrow { display: grid; grid-template-columns: 1fr 60px 26px; gap: 6px; align-items: center;
  line-height: 1.5; }
.skillname { color: var(--window-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.skillbar { height: 5px; background: var(--stone-recess); border: 1px solid var(--stone-line); display: block; }
.skillbar i { display: block; height: 100%; background: var(--stone-accent); }
.skillrank { color: var(--ink-soft); text-align: right; }

/* ===================== key bindings ===================== */
#keysWin { width: 320px; }
#keysTools { text-align: left; border: 0; padding: 0 2px 4px; }
#keysHint { color: var(--ink-soft); margin-left: 6px; font-size: 10px; }
#keysBody { max-height: 420px; overflow-y: auto; padding: 2px 4px 6px; }
.keyrow { display: grid; grid-template-columns: 1fr auto 18px; gap: 6px; align-items: center;
  line-height: 1.7; cursor: pointer; }
.keyrow:hover { background: var(--stone-hover); }
/* The row waiting for a key is unmistakable, because everything else on the keyboard is
   temporarily meaningless while it waits. */
.keyrow.listening { background: var(--stone-sel); outline: 1px solid var(--ink-strong); outline-offset: -1px; }
.keyname { color: var(--window-text); }
.keyhold { color: #7d8a6a; font-style: normal; font-size: 9px; }
.keycode { color: var(--ink-strong); min-width: 62px; text-align: right; }
.keycode.unbound { color: var(--ink-dim); }
.keyclear { padding: 0 4px; font-size: 10px; line-height: 1.2; }

/* ===================== inventory ===================== */
#invWin { width: 470px; }
#invBody { display: grid; grid-template-columns: 140px 1fr 1fr; gap: 0 10px; }
#dollCol { grid-row: 1; }
#invDollNote { font-size: 9px; color: var(--ink-dim); text-align: center; }
#equipCol, #packCol { max-height: 400px; overflow-y: auto; }
.packmeta { color: var(--ink-soft); font-weight: normal; }

/* Empty rows keep the pack's shape, so a nearly-full bag still has somewhere to click a held item
   into (card NORRCURSOR) and the column does not jump about as things come and go. */
.itemrow.packempty, .slotrow.vacant { opacity: 0.4; }

/* While something is in the air, every slot says whether it would take it. Answering "where
   does this go" mid-drag is most of what makes a pack usable. */
.slotrow.willtake {
  background: color-mix(in srgb, var(--stone-sel) 70%, green);
  outline: 1px solid color-mix(in srgb, green 50%, var(--ink-strong)); outline-offset: -1px;
}
.slotrow.wonttake { opacity: 0.28; }
.slotrow.willtake .slotname { color: #2f6e2f; }

.itemrow:hover, .slotrow:hover { background: var(--stone-hover); }
.itemrow.packempty:hover { background: none; }

/* The coin line, with the instructions kept quiet beneath it. */
#coinRow { display: flex; flex-direction: column; gap: 1px; padding: 4px 4px 2px;
  border-top: 1px solid var(--stone-line); margin-top: 4px; }
.coinamt { color: var(--ink-strong); }
.coinhint { color: var(--ink-dim); font-size: 10px; }

/* ===================== build picker ===================== */
.genderrow { display: flex; gap: 4px; margin-bottom: 3px; }
.genderbtn { flex: 1 1 0; padding: 3px 6px; }
.genderbtn.on { background: var(--stone-sel); border-color: #c9a84c; color: var(--ink-strong); }
#genderNote { color: var(--ink-dim); font-size: 10px; min-height: 13px; margin-bottom: 4px; }

/* ---------- talking, and handing things over ---------- */
/* A bracketed word in what somebody says. You could always just type it -- that is the game --
   but a word you can see is a word you know you spelled right. Ink on parchment, so the link
   is a darker underlined word rather than a glow. */
.saylink { color: var(--parchment-text); font-weight: bold; text-decoration: underline dotted; cursor: pointer; }
.saylink:hover { background: color-mix(in srgb, var(--parchment-fill) 78%, black); }

#tradeWin { width: 260px; }
#tradeWho { font-size: 11px; color: var(--ink-soft); margin-bottom: 6px; }
#tradeSlots { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.tradeslot {
  border: 1px solid var(--stone-line); background: var(--stone-recess); min-height: 34px;
  display: flex; align-items: center; gap: 4px; padding: 3px 4px; font-size: 11px;
  cursor: pointer;
}
.tradeslot.empty { color: var(--ink-dim); justify-content: center; }
.tradeslot.wanted { border-color: #7a9a4a; background: #101a08; }
#tradeHint { font-size: 10px; color: var(--ink-dim); margin: 6px 0 0; min-height: 24px; }

/* The key that uses a skill, shown on the skill's own row -- the keybind list is not where
   anybody looks for "what can this character do". */
.skillkey {
  font-size: 9px; color: var(--recess-text); background: var(--stone-recess); border-radius: 2px;
  padding: 0 3px; margin-left: 5px; vertical-align: middle;
}

/* ---------- stack counts ---------- */
/* The number in the corner of the picture. Only ever drawn for two or more: an "x1" on every
   icon is noise that hides the counts that matter. */
.stack { position: relative; display: inline-block; flex: none; line-height: 0; }
.stackn {
  position: absolute; right: -2px; bottom: -2px;
  font-size: 9px; line-height: 1; font-weight: bold;
  color: #fff2c8; background: rgba(10, 8, 5, 0.85);
  border: 1px solid var(--stone-line); border-radius: 2px; padding: 0 2px;
  pointer-events: none;
}

/* ---------- the two small questions ---------- */
.promptveil {
  position: absolute; inset: 0; z-index: 200;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: center; justify-content: center;
}
.promptbox { width: 250px; }
.promptlabel { font-size: 11px; color: var(--ink-soft); margin-bottom: 8px; line-height: 1.5; }
.promptrow { display: flex; align-items: center; gap: 6px; }
.promptrow input[type=range] { flex: 1; min-width: 0; }
.promptnum {
  width: 52px; text-align: right; background: var(--stone-recess); color: var(--recess-text);
  border: 1px solid var(--stone-line); padding: 2px 4px; font: inherit;
}
.promptnote { font-size: 10px; color: var(--ink-dim); text-align: right; margin-top: 2px; }
button.danger { color: #ffd0d0; border-color: #6a2020; background: #2a0e0e; }
button.danger:hover { background: #4a1414; }

/* ---------- right-click menu on an item ---------- */
.itemmenu {
  position: absolute; z-index: 210; min-width: 128px;
  border: 1px solid var(--stone-line);
  background-color: var(--window-fill);
  background-image: var(--stone-grain);
  box-shadow: inset 1px 1px 0 var(--etch), 0 2px 8px rgba(0, 0, 0, 0.6);
}
.itemmenu .mtitle {
  font-size: 10px; color: var(--ink-dim); padding: 3px 8px; border-bottom: 1px solid var(--stone-dark);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px;
}
.itemmenu button {
  display: block; width: 100%; text-align: left; border: 0; background: transparent;
  box-shadow: none; text-shadow: 0 1px 0 var(--etch);
  color: var(--window-text); padding: 4px 8px; font: inherit; cursor: pointer;
}
.itemmenu button:hover { background: var(--stone-hover); color: var(--ink-strong); }
.itemmenu button.danger { color: #ff9f9f; background: transparent; border: 0; }
.itemmenu button.danger:hover { background: #4a1414; }

/* ---------- the hover card ---------- */
/* What an item is, without having to open anything. Follows the pointer and never takes it.
   Drawn as a small parchment note, the way the reference draws item inspection. */
#itemTip {
  position: absolute; z-index: 190; pointer-events: none;
  max-width: 230px; border: 1px solid var(--parchment-line);
  background-color: var(--parchment-fill);
  background-image: var(--stone-grain);
  border-radius: 4px;
  box-shadow: inset 0 0 14px rgba(60, 40, 10, 0.28), 0 2px 6px rgba(0, 0, 0, 0.5);
  padding: 5px 7px; font-size: 11px; line-height: 1.45; color: var(--parchment-text);
}
#itemTip .tipname { color: var(--parchment-text); font-weight: bold; }
#itemTip .tipname.magic { color: color-mix(in srgb, var(--bar-mana) 70%, var(--parchment-text)); }
#itemTip .tipstat { color: color-mix(in srgb, var(--parchment-text) 80%, var(--parchment-fill)); }
#itemTip .tiphint { color: color-mix(in srgb, var(--parchment-text) 62%, var(--parchment-fill)); font-size: 10px; margin-top: 3px; }

/* ---------- the cursor hand (card NORRCURSOR) ---------- */
/* What is on the cursor, following the pointer everywhere it goes. pointer-events: none so it
   never steals a click meant for whatever is actually under the pointer -- this is a picture of
   what your hand holds, not a real object the DOM can be clicked through. */
#cursorHold { position: absolute; z-index: 220; pointer-events: none; }

/* ---------- merchant selection ---------- */
.itemrow.sel { background: var(--stone-sel); }
.itemrow.sel .itemname { color: var(--ink-strong); }

/* ---------- the creation description, in four parts ---------- */
/* Class, race, deity and city. Each follows its own choice, so changing one does not rewrite
   the others -- which is what makes the panel worth reading while you are still deciding. */
#createDesc { max-height: 340px; overflow-y: auto; }
.descpart { border-top: 1px solid var(--stone-line); padding: 5px 0; }
.descpart:first-child { border-top: 0; padding-top: 0; }
.deschead {
  font-size: 9px; letter-spacing: 1px; text-transform: uppercase; color: var(--ink-dim);
}
.desctitle { color: var(--ink-strong); font-weight: bold; }
.descbody { color: var(--ink-soft); margin: 2px 0; }
.descmeta { color: var(--ink-soft); font-size: 10px; }
.citylist { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 4px; }
.cityname {
  font-size: 10px; padding: 1px 4px; border: 1px solid var(--stone-line); color: var(--ink-soft);
}
.cityname.here { border-color: var(--ink-strong); color: var(--ink-strong); }
.cityname.shut { color: var(--ink-dim); border-color: #262114; }
.citynote { font-size: 9px; color: var(--ink-dim); margin-top: 3px; }

/* A box with no title bar is dragged by its own background, so the HUD can be arranged too. */
.dragbody { cursor: move; }
.dragbody button, .dragbody input, .dragbody canvas, .dragbody .gem,
.dragbody .hotslot, .dragbody .itemrow, .dragbody .bar { cursor: default; }

/* ---------- journal ---------- */
/* Folded up by default: an entry you are working on should not be buried under four you are
   not. The name is always there; everything else is behind a click. */
.questrow { cursor: pointer; }
.questfold { color: var(--ink-dim); display: inline-block; width: 12px; }
.questrow.open .questfold { color: var(--ink-strong); }
.questnote {
  color: var(--ink-soft); font-style: italic; margin: 3px 0 4px 12px;
  border-left: 2px solid var(--stone-dark); padding-left: 6px; line-height: 1.5;
}

/* ---------- on-screen controls ---------- */
/* A phone has no keyboard, no right button and no pointer lock. These hold the same virtual keys
   the keyboard would, so nothing downstream knows the difference. */
#touchLayer { position: absolute; inset: 0; z-index: 120; pointer-events: none; }
#touchLayer.hidden { display: none; }
#touchLayer > * { pointer-events: auto; }

#touchStick {
  position: absolute;
  left: calc(18px + env(safe-area-inset-left));
  bottom: calc(22px + env(safe-area-inset-bottom));
  width: 132px; height: 132px; border-radius: 50%;
  border: 2px solid rgba(200, 178, 120, 0.45);
  background: radial-gradient(circle, rgba(20,16,8,0.55), rgba(10,8,5,0.30));
  touch-action: none;
}
#touchKnob {
  position: absolute; left: 50%; top: 50%; width: 54px; height: 54px; margin: -27px 0 0 -27px;
  border-radius: 50%; background: rgba(200, 178, 120, 0.55);
  border: 2px solid rgba(255, 230, 160, 0.7);
  transition: transform 0.06s linear;
}

#touchButtons {
  position: absolute; right: 16px; bottom: 22px;
  display: grid; grid-template-columns: 62px 62px; gap: 10px;
}
.touchbtn {
  width: 62px; height: 62px; border-radius: 50%;
  font-size: 24px; line-height: 1; padding: 0;
  border: 2px solid rgba(200, 178, 120, 0.5);
  background: rgba(20, 16, 8, 0.6); color: var(--window-text);
  touch-action: none; user-select: none;
}
.touchbtn.held { background: rgba(90, 74, 32, 0.9); color: var(--ink-strong); }

/* A one-time teaching line. It floats low-centre over the world -- clear of the top HUD strip and
   the thumbs -- and fades the moment you first drag to look, which is the thing it is asking for. */
#touchHint {
  position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%);
  text-align: center;
  font-size: 11px; color: rgba(200, 178, 120, 0.6); pointer-events: none;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9); transition: opacity 0.5s ease;
}
body.looked #touchHint { opacity: 0; }

#touchButtons {
  position: absolute;
  right: calc(12px + env(safe-area-inset-right));
  bottom: calc(22px + env(safe-area-inset-bottom));
  display: grid; grid-template-columns: repeat(3, 54px); gap: 8px;
}
.touchbtn { width: 54px; height: 54px; }

/* The action bar's drawer handle: a tab on the right edge, halfway down, where a right thumb
   already is. */
#touchDrawer {
  position: absolute; right: env(safe-area-inset-right); top: 46%;
  width: 22px; height: 54px; padding: 0;
  border: 1px solid rgba(200, 178, 120, 0.5); border-right: none;
  border-radius: 6px 0 0 6px;
  background: rgba(20, 16, 8, 0.66); color: var(--window-text); font-size: 13px;
  touch-action: none; user-select: none;
}

/* =====================================================================
   MOBILE LAYOUT
   The desktop HUD is five boxes at fixed pixel sizes -- a 480px chat log, a ten-button action
   bar, a compass in the bottom right corner where a thumb now lives. On a 390px phone they land
   on top of each other and on the controls.
   So in touch mode every piece gets its own zone, sized in vw rather than px, and the two big
   ones are put away behind a tap. What stays on screen is what you cannot play without: your
   bars, your target, your heading, and your thumbs.
   ===================================================================== */

/* Under the notch and above the home bar. */
body.touchmode #hud {
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}

/* --- the top strip: you, your target, your heading, side by side --- */
/* Three lanes across the top, and they must not touch: the target box is centred, so the two
   outer boxes have to leave it more than its own width between them. */
body.touchmode #playerWin {
  left: 4px; top: 4px; width: 34vw; min-width: 126px; max-width: 214px; padding: 3px 5px;
}
body.touchmode #targetWin {
  left: 50%; top: 4px; width: 26vw; min-width: 100px; max-width: 172px; padding: 3px 5px;
}
/* Left of the menu button, not under it. The menu button is drawn on top and owns the corner. */
body.touchmode #compass {
  right: 58px; top: 4px; bottom: auto; width: 20vw; min-width: 72px; max-width: 116px;
  padding: 2px 4px;
}
body.touchmode #compassHeading { font-size: 17px; }
body.touchmode .barrow { margin: 1px 0; }
body.touchmode .bar { height: 11px; }
body.touchmode .bartext, body.touchmode .barlabel { font-size: 9px; }
/* Six attribute numbers you read once an hour are not worth a strip of a phone screen. They are
   two taps away in the character sheet. */
body.touchmode #pStats { display: none; }

/* Spell gems stack down the right edge under the compass, out of the thumbs' way. hud.js already
   hides the whole bar when you have no gems, so this costs a caster nothing and everyone else
   nothing at all. */
body.touchmode #gemBar {
  right: 4px; top: 62px; width: 46px; padding: 2px;
}

/* --- chat: a few lines by default, the whole log on a tap --- */
body.touchmode #chatWin {
  left: 4px; bottom: 168px; width: 56vw; min-width: 200px; max-width: 420px;
  height: 82px; background: color-mix(in srgb, var(--parchment-fill) 78%, transparent); border-color: var(--parchment-line);
  font-size: 11px;
}
body.touchmode #chatLog { font-size: 11px; line-height: 1.3; }
/* Expanded, it takes the width -- but not the strip the drawer tab lives in, and not the rail
   itself when the rail is out. A log you cannot close because the tab is underneath it is worse
   than a short log. */
body.touchmode.chatopen #chatWin {
  left: 4px; right: 30px; top: 152px; bottom: 168px; width: auto; max-width: none;
  height: auto; background: color-mix(in srgb, var(--parchment-fill) 94%, transparent); border-color: var(--parchment-line);
}
body.touchmode.chatopen.barsopen #chatWin { right: 112px; }

/* --- the action bar, in its drawer --- */
/* Hung from the top rather than centred, and stopped well above the button cluster: centring a
   ten-button column means its length decides whether it lands on your thumbs, and on a short
   screen it does. */
/* Not wrapped here: as a column with a height cap, wrapping would fold the buttons into a second
   column off the side of the screen instead of scrolling them. */
body.touchmode #hotbar {
  left: auto; right: 24px; bottom: auto; top: 96px;
  transform: none;
  flex-direction: column; flex-wrap: nowrap; gap: 4px;
  max-height: calc(100vh - 230px); overflow-y: auto;
}
body.touchmode:not(.barsopen) #hotbar { display: none; }
body.touchmode .hotslot { min-width: 66px; min-height: 34px; font-size: 12px; }

/* --- popups become sheets --- */
/* !important because windowframe.js writes a remembered desktop position straight onto the
   element, and a window remembered at x=980 on a 390px screen is a window you cannot see. The
   inline styles are left alone, so turning touch mode off restores the desktop layout exactly. */
/* The dev menu is the only way to reach the stats box on a phone -- there is no backtick key --
   so it gets the sheet treatment along with everything else. */
body.touchmode .eqwin.popup,
body.touchmode #mainMenu,
body.touchmode #devMenu,
body.touchmode #optionsPanel {
  left: 3vw !important; right: 3vw !important; top: 6vh !important; bottom: auto !important;
  width: auto !important; max-width: none !important; height: auto !important;
  max-height: 82vh !important; transform: none !important;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
}
/* A grip you drag with a mouse is meaningless on a sheet that is already the screen's width. The
   sheet rule below sets width/height with !important precisely so a remembered desktop size
   cannot reach a phone, so a resize here could not take effect even if a finger could start one.
   Hidden rather than enlarged to a tap target: a control that is easy to hit and does nothing is
   worse than no control. */
body.touchmode .resizable::after { display: none; }
/* The paperdoll and the two item columns wrap instead of running off the edge. */
body.touchmode #invBody, body.touchmode #merchantBody { flex-wrap: wrap; }
body.touchmode #mapCanvas { flex: 1; width: 100%; min-height: 0; }

/* With a finger driving, the crosshair is meaningless. */
body.touchmode #crosshair { display: none; }

/* ---------- tap targets ----------
   A mouse pointer is one pixel and a fingertip is about forty. Every control below was built
   for the pointer -- a 21px close button, a 13px checkbox, a 4px music toggle -- and none of
   them are reliably hittable with a thumb.

   44px is Apple's documented minimum and the number used throughout here. Material asks for 48;
   the extra four go on every row of a list, and vertical space is the scarcest thing on a phone,
   so 44 it is.

   The rule is applied to the thing that HANDLES the tap, not to the thing you can see. A caret
   inside a row stays a caret; the row grows. */
body.touchmode {
  --tap: 44px;
}

/* Rows in every list: inventory, equipment, spells, merchant stock, loot, faction. */
body.touchmode .itemrow,
body.touchmode .slotrow,
body.touchmode .spellrow,
body.touchmode .pick,
body.touchmode .gem {
  min-height: var(--tap);
  align-items: center;
  padding-left: 6px; padding-right: 6px;
}
body.touchmode .pick { display: flex; }
body.touchmode .itemrow, body.touchmode .slotrow, body.touchmode .spellrow {
  font-size: 13px;
}

/* Buttons, and the window close box. The close box was 32x21 in the corner of a title bar --
   the single most-tapped and least-hittable control in the game. */
body.touchmode button { min-height: var(--tap); padding-left: 12px; padding-right: 12px; }
body.touchmode .closex {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap); height: var(--tap);
  margin: -8px -6px -8px 0; padding: 0; font-size: 20px;
}
body.touchmode .wintitle { min-height: 30px; display: flex; align-items: center; }
body.touchmode .wintitle .closex { margin-left: auto; float: none; }
body.touchmode #menuButton { width: var(--tap); height: var(--tap); font-size: 24px; line-height: 44px; }

/* The music toggle renders as a 4px sliver under the compass. */
body.touchmode #musicToggle {
  min-height: var(--tap); display: flex; align-items: center; justify-content: center;
  font-size: 12px;
}

/* Journal entries fold open on a tap on their name. The caret is 12x14; the name row is what
   has to be big. */
body.touchmode .questname { min-height: var(--tap); display: flex; align-items: center; }
body.touchmode .questrow { padding: 2px 4px; }

/* Options. A native checkbox cannot be made 44px without looking like a novelty, so the LABEL
   is the target -- clicking a label toggles the box it wraps -- and the box itself just grows
   enough to be read. */
body.touchmode .optrow label,
body.touchmode .optgrouphead,
body.touchmode #menuTouch {
  min-height: var(--tap); display: flex; align-items: center; font-size: 13px;
}
body.touchmode input[type="checkbox"] { width: 22px; height: 22px; flex: none; }
body.touchmode .optlabel { font-size: 13px; }

/* A slider you drag with a thumb needs a thumb-sized grip and a lane tall enough to catch the
   press that starts the drag. The track stays thin; the input around it does not. */
body.touchmode input[type="range"] {
  height: var(--tap); -webkit-appearance: none; appearance: none; background: transparent;
}
body.touchmode input[type="range"]::-webkit-slider-runnable-track {
  height: 6px; background: #3a3018; border: 1px solid var(--stone-line); border-radius: 3px;
}
body.touchmode input[type="range"]::-moz-range-track {
  height: 6px; background: #3a3018; border: 1px solid var(--stone-line); border-radius: 3px;
}
body.touchmode input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; margin-top: -11px; border-radius: 50%;
  background: #d8c070; border: 2px solid var(--window-frame);
}
body.touchmode input[type="range"]::-moz-range-thumb {
  width: 26px; height: 26px; border-radius: 50%;
  background: #d8c070; border: 2px solid var(--window-frame);
}

/* The rest of the tappable surfaces, found by walking the page and measuring rather than by
   remembering: trade slots, the character roster, keybind rows, the item context menu, the
   trigger words in what an NPC says, and the swatches on the creation screen. */
body.touchmode .tradeslot,
body.touchmode .charrow,
body.touchmode .keyrow,
body.touchmode .itemmenu button,
body.touchmode .devcheck {
  min-height: var(--tap);
}
body.touchmode .tradeslot, body.touchmode .charrow { align-items: center; }
/* A word you can ask an NPC about, sitting inside a line of chat. It cannot be 44px tall without
   pushing the lines apart, so it gets width and a bigger face instead. */
body.touchmode .saylink { padding: 4px 6px; font-size: 13px; }
body.touchmode #chatLog { line-height: 1.6; }
body.touchmode .swatch { width: 32px; height: 32px; }

/* The other half of an affordance: something has to happen when you touch it.
   A mouse has hover, so every row in this UI lights up before you commit to it and you know it
   is a control. A finger has no hover at all -- the first feedback you get is the action itself,
   and if you missed you get nothing and cannot tell whether the game is broken or your thumb
   was. So the hover state is re-bound to the press. */
body.touchmode .itemrow:active,
body.touchmode .slotrow:active,
body.touchmode .spellrow:active,
body.touchmode .pick:active,
body.touchmode .gem:active,
body.touchmode .charrow:active,
body.touchmode .keyrow:active,
body.touchmode .questname:active,
body.touchmode .tradeslot:active,
body.touchmode .optgrouphead:active,
body.touchmode .optrow label:active,
body.touchmode #musicToggle:active {
  background: var(--stone-sel); color: var(--ink-strong);
}
body.touchmode button:active, body.touchmode .hotslot:active, body.touchmode .closex:active {
  background: #5a4a20; color: var(--ink-strong);
}
/* And the grey flash Safari and Chrome paint over anything tapped, which fires on the whole
   window when you drag to look and reads as the UI glitching. */
body.touchmode, body.touchmode * { -webkit-tap-highlight-color: transparent; }

/* Text you type into. Below 16px iOS zooms the whole page on focus, which throws the layout
   away and cannot be undone by hand. */
body.touchmode input[type="text"], body.touchmode #chatInput, body.touchmode #nameField {
  min-height: var(--tap); font-size: 16px;
}

/* An older or smaller phone -- 320 to 360 css pixels across. Three boxes will not fit in a row
   that narrow, and neither will a 132px stick beside a 178px button cluster. The target box drops
   to its own line and the controls come in. */
@media (max-width: 360px) {
  /* Under the player box rather than beside it -- three boxes will not fit across 320 pixels.
     Clear of the bars below their tallest state, which is with a buff row showing. */
  body.touchmode #targetWin {
    left: 4px; top: 106px; transform: none;
    width: 52vw; min-width: 0; max-width: none;
  }
  body.touchmode #touchStick { left: 10px; bottom: 18px; width: 108px; height: 108px; }
  body.touchmode #touchButtons {
    right: 8px; bottom: 18px; grid-template-columns: repeat(3, 46px); gap: 6px;
  }
  body.touchmode .touchbtn { width: 46px; height: 46px; font-size: 20px; }
  body.touchmode #chatWin { width: 60vw; bottom: 150px; height: 74px; }
  body.touchmode.chatopen #chatWin { top: 180px; bottom: 150px; height: auto; }
  body.touchmode .hotslot { min-width: 60px; font-size: 11px; }
}

/* A phone held sideways has 390 pixels of height and the controls want most of it. Everything
   that is merely nice to look at gets smaller or goes. */
@media (max-height: 480px) {
  body.touchmode #chatWin { height: 60px; bottom: 150px; }
  /* It grows upward from where it already sits rather than downward into the controls, and stops
     short of the gem bar on the right. */
  body.touchmode.chatopen #chatWin {
    top: 104px; bottom: 150px; right: 60px; height: auto;
  }
  /* Ten buttons in a column is taller than the screen is, so sideways the action bar stops being
     a rail and becomes a panel over the middle of the world. That is what it is: a menu. */
  body.touchmode #hotbar {
    left: 50%; right: auto; top: 50%; transform: translate(-50%, -50%);
    display: grid; grid-template-columns: repeat(3, 72px); gap: 5px;
    max-height: none; overflow: visible;
  }
  /* And while that menu is up, the chat waits its turn. */
  body.touchmode.barsopen #chatWin { display: none; }
  body.touchmode #buffRow { display: none; }
  body.touchmode #touchStick { width: 112px; height: 112px; left: 12px; bottom: 14px; }
  body.touchmode #touchButtons { bottom: 14px; grid-template-columns: repeat(3, 48px); gap: 6px; }
  body.touchmode .touchbtn { width: 48px; height: 48px; font-size: 21px; }
  body.touchmode .eqwin.popup,
  body.touchmode #mainMenu,
  body.touchmode #optionsPanel { top: 2vh !important; max-height: 94vh !important; }
}

/* The menu's own mobile toggle. Top level, because somebody who cannot control the game needs
   it in one tap. */
#menuTouch {
  display: block; padding: 5px 2px; font-size: 12px; color: var(--window-text); cursor: pointer;
}
#menuTouch input { margin-right: 6px; }

@media (max-width: 820px) {
  /* On a phone the popups are the whole screen or they are unusable. */
  .popup { max-width: 94vw; }
  #invWin { width: 94vw; }
  #merchantWin { width: 94vw; }
}

/* ---------- character creation on a narrow screen ---------- */
/* The columns wrap to one, which turns a 763px panel into a 1900px scroll -- and puts "Enter
   World" a thousand pixels below the fold. The buttons stick to the bottom of the panel instead,
   so the way out is always on screen however far down the list you are. */
@media (max-width: 820px) {
  #createBox { display: flex; flex-direction: column; max-height: 94vh; }
  #createBody { overflow-y: auto; flex: 1; min-height: 0; -webkit-overflow-scrolling: touch; }
  /* A sibling of the scrolling body now, so it is simply the panel's footer -- sticky inside a
     tall column pinned it to the bottom of the COLUMN, which was off screen. */
  #createStart {
    margin: 0; padding: 8px 0 2px; flex: none;
    display: flex; gap: 8px; justify-content: stretch;
    border-top: 1px solid var(--stone-line);
  }
  #createStart button { flex: 1; padding: 14px 8px; font-size: 15px; min-height: 48px; }
  /* The description panel is the longest thing here and the least urgent. */
  #createDesc { max-height: 190px; }
  /* Bigger targets for a thumb. Same 44px floor the in-game UI uses: this screen is the first
     thing a phone sees, and a race list you cannot reliably tap is where it stops. */
  .pick { min-height: 44px; display: flex; align-items: center; padding: 4px 8px; font-size: 14px; }
  .picklist { max-height: 220px; }
  .genderbtn, .statbtn, .headpick button { min-height: 44px; min-width: 44px; font-size: 15px; }
  #getNameBtn { min-height: 44px; min-width: 44px; font-size: 15px; }
  .headpick .hpname { font-size: 13px; }
  .swatch { width: 34px; height: 34px; }
  input, select { min-height: 44px; font-size: 16px; }
}

/* ---------- the performance readout ---------- */
/* Deliberately plain and monospaced, like the dev menu: it is an instrument, not game UI. Sits
   top-left under the bars, narrow enough to leave the world visible behind it. */
#perfHud {
  position: absolute; left: 4px; top: 108px; z-index: 130;
  width: 178px; padding: 5px 7px;
  /* With the graphs on this box gets tall. It must never run off the bottom of the screen --
     it cannot be scrolled, because taking pointer events would eat the drag that looks around. */
  max-height: calc(100vh - 190px); overflow: hidden;
  background: rgba(6, 10, 14, 0.86); border: 1px solid #3d4a55;
  font: 11px/1.35 ui-monospace, Menlo, Consolas, monospace;
  color: #b9c6d2; pointer-events: none; user-select: none;
}
#perfHud.hidden { display: none; }
.perfhead {
  color: #fff; font-size: 13px; font-weight: bold; margin-bottom: 3px;
  border-bottom: 1px solid #3d4a55; padding-bottom: 3px;
}
.perfgrp {
  color: #6f8296; text-transform: uppercase; letter-spacing: 1px; font-size: 9px;
  margin: 5px 0 1px;
}
.perfrow { display: flex; justify-content: space-between; gap: 6px; }
.perfrow b { font-weight: normal; }
.perfnote { margin-top: 4px; color: #8fd0ff; white-space: normal; }

/* The severity ladder. Not a gradient -- each step is a different KIND of bad, and the last two
   are colours that have no business on a status readout, which is the point: by the time a
   number is purple it is not a slow frame, it is broken. */
.g0 { color: #e6eef5; }                                  /* normal */
.g1 { color: #ffe066; }                                  /* concerning */
.g2 { color: #ff9d3d; }                                  /* problematic */
.g3 { color: #ff4d4d; }                                  /* terrible */
.g4 { color: #c46bff; text-shadow: 0 0 6px #c46bff88; }  /* extremely terrible */
.g5 { color: #ff3fa4; text-shadow: 0 0 8px #ff3fa4aa; font-weight: bold; }  /* horrible */

/* Sparklines. The label and the peak sit on top of the trace rather than beside it, because the
   box is 178px wide and a graph narrower than the numbers is not a graph. */
.perfgraph { position: relative; height: 22px; margin: 3px 0 5px; }
.perfgraph canvas { display: block; width: 164px; height: 22px; }
.perfglabel {
  position: absolute; left: 2px; top: 0; font-size: 9px; color: #6f8296; pointer-events: none;
}
.perfgmax {
  position: absolute; right: 2px; top: 0; font-size: 9px; pointer-events: none;
}
/* Sideways on a phone the bars are shorter, so it can sit higher. */
@media (max-height: 480px) {
  #perfHud { top: 76px; font-size: 10px; width: 164px; max-height: calc(100vh - 110px); }
  /* Sideways there is room for the two graphs that answer "was that a spike", and not for the
     two that only ever show a flat line while you stand still. */
  .perfgraph:nth-of-type(n+3) { display: none; }
  .perfgraph { height: 18px; }
  .perfgraph canvas { height: 18px; }
}
.devrow { display: flex; align-items: center; gap: 6px; margin: 2px 0; }
.devrow button { flex: 0 0 auto; min-width: 96px; }
.devnote { color: #6f8296; font-size: 10px; }
.devcaret { color: #6f8296; display: inline-block; width: 10px; }
.devtitle { cursor: pointer; }
.devtitle:hover { color: #cfe0ee; }
body.touchmode .devtitle, body.touchmode #devMenu button {
  min-height: var(--tap); display: flex; align-items: center;
}

/* The way out of being dead. A keyboard has Enter; a phone has this, and without it death is
   the one state in the game with no exit. */
#deathBtn {
  margin-top: 18px; padding: 12px 22px; font-size: 15px;
  background: linear-gradient(#4a1c1c, #2a0e0e); border: 2px solid #a05050; color: #ffd8d8;
}
#deathBtn:hover { background: linear-gradient(#5f2626, #3a1414); color: #fff; }
#deathBtn:active { background: #7a3030; color: #fff; }
body.touchmode #deathBtn { min-height: 56px; font-size: 17px; padding: 14px 28px; }

/* ---------- recessed surfaces keep light lettering ---------- */
/* A recess is cut deep into the stone and sits in shadow, so ink drawn down there has to be
   pale to read: the parchment-derived --recess-text, never the engraved surface inks. */
.picklist, #charList, #statBlock, .tradeslot { color: var(--recess-text); }
#statBlock .statname { color: color-mix(in srgb, var(--recess-text) 72%, transparent); }
#statBlock .statval { color: var(--recess-text); }
.picklist .pick.off { color: var(--recess-text); opacity: 0.4; }
.tradeslot.empty { color: var(--recess-text); opacity: 0.55; }

/* ---------- stone frame details (card NORRSTONE1999) ---------- */
/* The Main block draws as the reference's wide engraved rectangles. */
body.stonemode #mainButtons .mainbtn {
  width: 100%; min-height: 26px; font-size: 12px;
  font-variant: small-caps; font-weight: bold; letter-spacing: 1.5px;
}
/* In the gem strip the picture is the whole gem: round, ringed by a carved socket. */
body.stonemode #gemBar .gemicon {
  width: 26px; height: 26px; flex: 0 0 26px; border-radius: 50%;
  border: 2px solid var(--stone-line);
  box-shadow:
    inset -2px -2px 4px rgba(0, 0, 0, 0.6),
    0 1px 0 var(--etch), 0 0 0 1px rgba(0, 0, 0, 0.35);
  background: var(--stone-recess);
}
body.stonemode #gemBar .gemorb { width: 26px; height: 26px; }
/* The player and target names read as the reference paints them: pale lettering on the stone,
   not an engraving -- the one place the column uses light text. */
body.stonemode #playerWin .wintitle, body.stonemode #targetWin .wintitle {
  color: var(--recess-text); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
  border-bottom-color: var(--stone-dark); box-shadow: none;
}

/* ---------- the macro region (card NORRSTONE1999, second reference dump) ---------- */
/* The page flip stands above the open page (MECHANICS 23e): one engraved button, named for
   the page it turns to. Main is the manual's wide rectangles, the other pages draw two
   across like the reference's combat page. */
#mainButtons .macroflip {
  margin-bottom: 2px;
  font-variant: small-caps; letter-spacing: 1.5px; font-weight: bold;
}
.macrogrid { display: grid; grid-template-columns: 1fr 1fr; gap: 3px; margin-top: 3px; }
.macrogrid .macrocell {
  width: auto; min-height: 26px; font-size: 10px;
  font-variant: small-caps; font-weight: bold; letter-spacing: 0.5px;
}

/* Buffs hang as square tiles along the viewport's right edge, the reference's effect icons.
   A chip that carries an icon becomes the tile; a text-only status chip keeps its words. */
body.stonemode #buffRow {
  position: fixed; right: calc(var(--stone-right-w) + 8px); top: 8px;
  display: flex; flex-direction: column; align-items: flex-end; gap: 3px;
  margin: 0; z-index: 6;
}
body.stonemode #buffRow .buffchip:has(.bufficon) {
  width: 32px; height: 32px; justify-content: center; padding: 2px;
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  box-shadow: inset 1px 1px 0 var(--etch), inset -1px -1px 0 rgba(0, 0, 0, 0.45);
}
body.stonemode #buffRow .buffchip:has(.bufficon) .bufflabel,
body.stonemode #buffRow .buffchip:has(.bufficon) .buffticks { display: none; }
body.stonemode #buffRow .bufficon { width: 24px; height: 24px; flex: 0 0 24px; }

/* The plaque hangs the primary weapon, the way the reference hangs a sword there. */
#stonePlaque { display: flex; align-items: center; justify-content: center; }
.plaqueicon {
  width: 96px; height: 96px; flex: none;
  image-rendering: -webkit-optimize-contrast;
  filter: drop-shadow(2px 3px 3px rgba(0, 0, 0, 0.55));
}

/* ---------- the entry ritual: login and server select (MECHANICS 23e) ---------- */
/* The same quarried wall the select and creation screens stand on. Only one of the entry
   screens is ever unhidden at a time, so the z order between them never decides anything. */
#loginScreen, #serverScreen {
  position: absolute; inset: 0; z-index: 66;
  background-color: var(--window-fill);
  background-image:
    radial-gradient(ellipse at 50% 42%, transparent 45%, rgba(0, 0, 0, 0.42)),
    repeating-linear-gradient(180deg,
      transparent 0, transparent 115px,
      rgba(0, 0, 0, 0.3) 115px, rgba(0, 0, 0, 0.3) 117px,
      var(--etch) 117px, var(--etch) 118px),
    repeating-linear-gradient(90deg,
      transparent 0, transparent 235px,
      rgba(0, 0, 0, 0.22) 235px, rgba(0, 0, 0, 0.22) 237px,
      var(--etch) 237px, var(--etch) 238px),
    var(--stone-grain);
  display: flex; align-items: center; justify-content: center;
  /* Status gold is derived, not copied, so the authored palette stays the one source. */
  --status-gold: color-mix(in srgb, var(--parchment-fill) 62%, yellow);
  --status-red: #c03030;
}
#loginBox { width: min(440px, 92vw); max-height: 94vh; display: flex; flex-direction: column; }
#loginBody { display: flex; flex-direction: column; gap: 4px; padding: 6px 4px 2px; }
#loginBody input {
  font-family: var(--font-book); font-size: 15px; letter-spacing: 1px;
  background: var(--stone-recess); color: var(--recess-text);
  border: 1px solid var(--stone-line); border-radius: 2px; padding: 6px 8px;
  box-shadow: inset 3px 3px 8px rgba(0, 0, 0, 0.6), 0 1px 0 var(--emboss-hi);
}
#loginBody input:focus { outline: none; border-color: var(--stone-accent); }
/* One spot, two duties, exactly as the reference has it: gold while the machinery talks,
   red when it refuses. The height is reserved so the panel never jumps. */
#loginStatus {
  min-height: 30px; margin-top: 8px; padding: 4px 2px;
  font-family: var(--font-book); font-size: 13px; font-style: italic;
  color: var(--status-gold);
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.55);
}
#loginStatus.error { color: var(--status-red); font-style: normal; font-weight: bold; }
#loginHint { font-size: 10px; color: var(--ink-dim); font-style: italic; margin-bottom: 2px; }
#loginButtons, #serverButtons { margin-top: 10px; display: flex; gap: 6px; justify-content: flex-end; }
#loginVersion {
  /* Bottom-right: the dev build stamp already owns the reference's bottom-left corner. */
  position: absolute; right: 14px; bottom: 10px;
  font-family: var(--font-carved); font-size: 10px; letter-spacing: 1px;
  color: var(--ink-dim); text-shadow: 0 1px 0 var(--etch);
}
#serverBox { width: min(560px, 92vw); max-height: 94vh; display: flex; flex-direction: column; }
#serverHead, .serverrow {
  display: grid; grid-template-columns: 1fr 90px 70px; gap: 8px; align-items: center;
}
#serverHead {
  padding: 5px 10px 3px;
  font-family: var(--font-carved); font-variant: small-caps; letter-spacing: 1px;
  font-size: 11px; color: var(--ink-soft);
  border-bottom: 1px solid var(--stone-line);
  text-shadow: 0 1px 0 var(--etch);
}
#serverHead span:nth-child(2), #serverHead span:nth-child(3) { text-align: right; }
#serverList {
  border: 1px solid var(--stone-line); background: var(--stone-recess);
  min-height: 96px; max-height: 46vh; overflow-y: auto;
  box-shadow: inset 3px 3px 8px rgba(0, 0, 0, 0.6);
}
.serverrow {
  padding: 9px 10px; cursor: pointer;
  border-bottom: 1px solid var(--stone-dark); color: var(--recess-text);
}
.serverrow:hover { background: color-mix(in srgb, var(--stone-recess) 70%, white); }
.serverrow.on { background: var(--stone-sel); color: var(--ink-strong); }
.serverrow.on .srvname { font-weight: bold; }
.serverrow.down { opacity: 0.45; cursor: default; }
.serverrow.down:hover { background: transparent; }
.srvname { font-size: 13px; }
.srvplayers, .srvstatus { text-align: right; font-size: 12px; }
#serverStatusLine { min-height: 18px; margin-top: 6px; font-size: 12px; font-weight: bold; color: var(--status-red); }
#serverNote { font-size: 10px; color: var(--ink-dim); font-style: italic; margin-top: 2px; }
body.touchmode #loginBody input { font-size: 16px; }

/* ---------- the corpse takes the left side (MECHANICS 23e) ---------- */
/* While the loot window is up in the stone frame, the whole left column IS the corpse:
   its name engraved at the top, item slots two across below, LOOT ALL and DONE at the
   bottom. It lies over the rail, the gems and the hot buttons; closing hands them back. */
body.stonemode #lootWin {
  position: fixed !important;
  left: 0 !important; top: 0 !important; right: auto !important; bottom: auto !important;
  transform: none !important;
  width: var(--stone-left-w) !important; height: 100vh !important;
  z-index: 30;
  display: flex; flex-direction: column;
  border: none; border-right: 2px solid var(--stone-line); border-radius: 0;
  padding: 8px 10px 10px;
  background-color: var(--window-fill);
  background-image:
    repeating-linear-gradient(180deg,
      transparent 0, transparent 91px,
      rgba(0, 0, 0, 0.34) 91px, rgba(0, 0, 0, 0.34) 93px,
      var(--etch) 93px, var(--etch) 94px),
    var(--stone-grain);
  box-shadow:
    inset 2px 2px 0 var(--emboss-hi),
    inset -2px -2px 0 var(--emboss-lo),
    inset 0 0 34px rgba(0, 0, 0, 0.18);
}
body.stonemode #lootWin.hidden { display: none; }
/* The corpse's name reads as a carved plate; the corner X retires -- DONE is the way out,
   as the reference writes it. */
body.stonemode #lootWin .wintitle {
  flex: none; margin: 0 0 6px; padding: 6px 4px;
  font-family: var(--font-carved); font-variant: small-caps; letter-spacing: 1px;
  font-size: 13px; text-align: center;
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line); border-radius: 2px;
  box-shadow: inset 2px 2px 0 var(--emboss-hi), inset -2px -2px 0 var(--emboss-lo);
  text-shadow: 0 1px 0 var(--etch);
}
body.stonemode #lootWin .closex { display: none; }
body.stonemode #lootBody {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px; align-content: start;
  padding: 2px;
}
body.stonemode #lootBody::-webkit-scrollbar { width: 9px; }
body.stonemode #lootBody::-webkit-scrollbar-track { background: var(--stone-recess); }
body.stonemode #lootBody::-webkit-scrollbar-thumb {
  background: var(--stone-face); border: 1px solid var(--stone-line);
}
/* Each entry becomes a sunk slot: icon above, name in small letters beneath. */
body.stonemode #lootBody .itemrow {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; margin: 0; padding: 7px 3px 5px; min-height: 64px;
  background: var(--stone-recess); border: 1px solid var(--stone-line); border-radius: 2px;
  box-shadow: inset 3px 3px 8px rgba(0, 0, 0, 0.7), 0 1px 0 var(--emboss-hi);
  text-align: center;
}
body.stonemode #lootBody .itemrow:hover {
  background: color-mix(in srgb, var(--stone-recess) 70%, white);
}
body.stonemode #lootBody .itemrow .icon { width: 30px; height: 30px; flex: 0 0 30px; }
body.stonemode #lootBody .itemrow .itemname {
  flex: none; width: 100%; font-size: 10px; line-height: 1.2; color: var(--recess-text);
}
body.stonemode #lootBody .itemrow .itemqty { font-size: 9px; color: var(--recess-text); opacity: 0.8; }
/* Coin lies flat across both columns, and an empty corpse says so plainly. */
body.stonemode #lootBody [data-coin] { grid-column: 1 / -1; flex-direction: row; min-height: 34px; }
body.stonemode #lootBody .emptyslot { grid-column: 1 / -1; text-align: center; padding: 14px 4px; color: var(--recess-text); opacity: 0.6; font-style: italic; }
body.stonemode #lootWin .winbuttons { flex: none; margin-top: 8px; display: flex; gap: 6px; }
body.stonemode #lootWin .winbuttons button { flex: 1 1 auto; }

/* ---------- the parchment's second duty: the item display (MECHANICS 23e) ---------- */
/* Inspecting swaps the log for the item, on the same parchment; DONE, Escape, or opening
   the chat line hands the log back. Colors ride the parchment palette, not the stone's. */
#chatWin.inspecting #chatLog { display: none; }
#inspectPane { height: 100%; min-height: 0; display: flex; flex-direction: column; }
#inspectPane.hidden { display: none; }
#chatWin:has(#chatInput:not(.hidden)) #inspectPane { height: calc(100% - 26px); }
#inspectDone {
  align-self: flex-end; flex: none; margin: 0 2px 2px; padding: 2px 12px;
  font-family: var(--font-carved); font-variant: small-caps; letter-spacing: 1px;
  font-size: 11px; cursor: pointer; border-radius: 2px;
  background-color: var(--stone-face);
  background-image: var(--stone-grain);
  border: 1px solid var(--stone-line); color: var(--ink);
  box-shadow: inset 2px 2px 0 var(--emboss-hi), inset -2px -2px 0 var(--emboss-lo);
  text-shadow: 0 1px 0 var(--etch);
}
#inspectDone:hover { background-color: var(--stone-hover); color: var(--ink-strong); }
#inspectBody {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 6px 4px 2px;
  font-family: var(--font-book); color: var(--parchment-text);
  column-width: 250px; column-gap: 20px;
}
#inspectBody::-webkit-scrollbar { width: 9px; }
#inspectBody::-webkit-scrollbar-track { background: color-mix(in srgb, var(--parchment-fill) 80%, black); }
#inspectBody::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--parchment-fill) 55%, var(--parchment-text));
  border-radius: 4px;
}
#inspectBody .exhead { column-span: all; }
#inspectBody .exname { color: var(--parchment-text); font-family: var(--font-carved); font-size: 14px; }
#inspectBody .exkind, #inspectBody .exflags {
  column-span: all; color: color-mix(in srgb, var(--parchment-text) 70%, var(--parchment-fill));
  font-style: italic; margin-bottom: 3px;
}
#inspectBody .exrow {
  border-bottom: 1px dotted color-mix(in srgb, var(--parchment-text) 30%, transparent);
  break-inside: avoid;
}
#inspectBody .exrow span:last-child, #inspectBody .exrow b {
  color: color-mix(in srgb, var(--parchment-text) 82%, var(--parchment-fill));
}
#inspectBody .exlore {
  column-span: all; margin-top: 5px; font-style: italic;
  color: color-mix(in srgb, var(--parchment-text) 75%, var(--parchment-fill));
}

/* The stale-bake warning (js/core/main.js showStaleBakeBanner). Deliberately the loudest thing on
   the screen: it means the game you are looking at is not the game on disk, and everything odd
   about it follows from that. Card NORRSTALEBAKE. */
#staleBake {
  position: fixed;
  left: 0; right: 0; top: 0;
  z-index: 99999;
  background: #7a1d1d;
  color: #ffe9e0;
  font: 13px/1.5 ui-monospace, Menlo, Consolas, monospace;
  padding: 8px 14px;
  border-bottom: 2px solid #40100f;
  text-align: center;
  pointer-events: none;
}
