/* MEFO — The Downfall of an Economy */

:root {
  --bg:      #1a1815;
  --panel:   #23201c;
  --panel2:  #2c2822;
  --ink:     #e8e2d6;
  --dim:     #9a9184;
  --line:    #3d372f;
  --gold:    #c9a227;
  --rm:      #d8cbb0;
  --mefo:    #d98f2b;
  --pol:     #9b7fc4;
  --good:    #6fae5a;
  --bad:     #c0504a;
  --mil:     #6b7a5a;
  --civ:     #4a7a8c;
  --inf:     #8c7a4a;
  --plt:     #7a5a8c;
  --lmk:     #a8863a;

  --turnbar: 56px;   /* height of the bottom turn-controls bar */
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 14px/1.5 "Iowan Old Style", "Palatino Linotype", Georgia, serif;
}

.hidden { display: none !important; }
.muted { color: var(--dim); }
.pad { padding: 8px 2px; }

button {
  font: inherit;
  font-size: 13px;
  padding: 7px 14px;
  border: 1px solid var(--line);
  background: var(--panel2);
  color: var(--ink);
  border-radius: 3px;
  cursor: pointer;
}
button:hover:not(:disabled) { border-color: var(--gold); color: #fff; }
button:disabled { opacity: .35; cursor: default; }
button.primary { background: var(--gold); color: #1a1815; border-color: var(--gold); font-weight: 700; }
button.primary:hover:not(:disabled) { background: #e0b52e; }
button.ghost { background: transparent; }
button.mini { padding: 3px 8px; font-size: 11px; }

/* ------------------------------------------------------------------ setup */

/* The main menu: title, choices and version down the left; news down the
   right; a rule between them. */
#setup { min-height: 100vh; }
.menu {
  display: grid;
  grid-template-columns: minmax(340px, 560px) minmax(0, 1fr);
  min-height: 100vh;
}
.menu-left {
  display: flex; flex-direction: column;
  padding: 40px 44px 22px;
  border-right: 2px solid var(--line);
  min-width: 0;
}
.menu-title {
  margin: 0;
  font-size: 46px; letter-spacing: 10px; line-height: 1.05;
  color: var(--gold);
}
.menu-title span {
  display: block;
  font-size: 13px; letter-spacing: 3px;
  color: var(--dim); margin-top: 8px;
}
.menu-body { flex: 1; padding: 9vh 0 24px; }
.menu-main { display: grid; gap: 10px; max-width: 360px; }
.menu-btn { padding: 13px 18px; font-size: 16px; letter-spacing: 1px; text-align: left; }
.menu-btn.active { border-color: var(--gold); }
.menu-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.menu-btn.small { padding: 9px 14px; font-size: 13px; text-align: center; }
/* The embedded bug report form fills the column; the form scrolls itself. */
.report { flex: 1; min-height: 0; display: flex; }
.report iframe { flex: 1; width: 100%; min-height: 480px; border: 0; border-radius: 4px; background: #fff; }
@media (max-width: 900px) { .report iframe { min-height: 85vh; } }

/* Lobby buttons in a row of fields: the same height as the fields beside them. */
.opts .inline-btn { align-self: flex-end; height: 34px; padding-top: 0; padding-bottom: 0; box-sizing: border-box; }

/* A labelled control that is not a text field, laid out like the fields
   beside it: a small label above, the control below. */
.field { display: grid; gap: 4px; align-content: start; }
.field-label { font-size: 12px; color: var(--dim); }
#lobbyPublicRow { margin: 6px 0 2px; }

/* A toggle switch that says what it is set to: "Private" in grey, or
   "Public" in green. The checkbox itself is hidden; the track shows it. Same
   height as a text field, so it sits level with one in a row. */
.switch { display: inline-flex; cursor: pointer; user-select: none; }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch .track {
  position: relative; display: block; width: 96px; height: 34px; box-sizing: border-box;
  background: #3a3630; border: 1px solid #55504a; border-radius: 17px;
  transition: background .2s, border-color .2s;
}
.switch .knob {
  position: absolute; top: 3px; left: 3px; width: 26px; height: 26px; border-radius: 50%;
  background: #a39d93; box-shadow: 0 1px 3px rgba(0, 0, 0, .4);
  transition: transform .2s ease, background .2s;
}
.switch .txt {
  position: absolute; top: 0; bottom: 0; display: flex; align-items: center;
  font-size: 12px; font-weight: 700; letter-spacing: .5px;
  transition: opacity .15s;
}
.switch .txt.off { right: 12px; color: #c9c3b8; }
.switch .txt.on  { left: 12px; color: #fff; opacity: 0; }
.switch input:checked + .track { background: #3d8b4f; border-color: #57a868; }
.switch input:checked + .track .knob { transform: translateX(62px); background: #fff; }
.switch input:checked + .track .txt.on  { opacity: 1; }
.switch input:checked + .track .txt.off { opacity: 0; }
.switch input:focus-visible + .track { outline: 2px solid var(--gold); outline-offset: 2px; }

/* The news archive: one row per earlier edition, newest first. */
.news-nav { display: flex; gap: 6px; }
.archive-list { display: grid; gap: 6px; }
.archive-row {
  display: flex; align-items: baseline; gap: 14px; text-align: left;
  padding: 10px 14px; background: var(--panel); font-size: 14px;
}
.archive-date { flex: none; width: 104px; font-size: 12px; color: var(--dim); }
.archive-title { color: var(--ink); }
.archive-stamp { margin: 0 0 10px; font-size: 12px; color: var(--dim); letter-spacing: 1px; }

.news-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.news code { font-size: 12px; color: var(--ink); }
.subpanel h2 { margin: 6px 0 4px; color: var(--gold); letter-spacing: 3px; font-size: 22px; }
.subpanel .back { padding: 4px 10px; font-size: 12px; }
.menu-foot { font-size: 11px; color: var(--dim); letter-spacing: 1px; }

.setrow {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 12px 0; border-bottom: 1px solid var(--line); cursor: pointer; max-width: 420px;
}
.setrow span { display: grid; }
.setrow i { font-style: normal; font-size: 12px; color: var(--dim); }
.setrow input { width: 18px; height: 18px; accent-color: var(--gold); }

.menu-right {
  display: flex; flex-direction: column;
  padding: 40px 44px 22px;
  min-width: 0; max-height: 100vh;
}
.news-head { margin: 0 0 14px; color: var(--gold); letter-spacing: 4px; font-size: 28px; }
.news { flex: 1; overflow-y: auto; padding-right: 8px; }
.news-line { position: relative; white-space: pre-wrap; tab-size: 4; min-height: 1.5em; overflow-wrap: anywhere; }
.news-pos { position: absolute; top: 0; white-space: pre; }
.news-img { max-width: 100%; height: auto; vertical-align: middle; }
.news a { color: #8fb8ff; }
.news .inert-link { cursor: default; }

@media (max-width: 900px) {
  .menu { grid-template-columns: 1fr; }
  .menu-left { border-right: 0; border-bottom: 2px solid var(--line); padding: 28px 18px 18px; }
  .menu-body { padding: 28px 0 20px; }
  .menu-main { max-width: none; }
  .menu-right { padding: 24px 18px 18px; max-height: none; }
  .news { overflow: visible; }
}
.blurb { color: var(--dim); border-left: 2px solid var(--line); padding-left: 14px; }
.blurb b { color: var(--ink); }

.opts { display: flex; flex-wrap: wrap; gap: 14px; margin: 22px 0 18px; }
.opts label { font-size: 12px; color: var(--dim); display: grid; gap: 4px; }
.opts label.chk { display: flex; align-items: center; gap: 7px; }
select, input[type=text], .seat-name {
  font: inherit; font-size: 13px;
  background: var(--panel2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 3px; padding: 6px 8px;
}

.seat { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.swatch.dim { background: var(--line); color: var(--dim); }
/* A black piece needs a rim to stay visible on a dark board. */
.swatch.rim { box-shadow: 0 0 0 1px var(--dim) inset; }

/* Hover help, used for roles and offices. */
.help { position: relative; cursor: help; }
.hint {
  display: inline-grid; place-items: center;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--line); color: var(--dim);
  font-size: 9px; vertical-align: middle;
}
.help:hover .hint { background: var(--gold); color: #1a1815; }
/* Drops below the trigger. Above would run off the top of the screen for the
   first player card, and the scrolling sidebar clips anything that overflows. */
.tip {
  position: absolute; left: 0; top: 150%; z-index: 80;
  display: none; width: 260px; padding: 9px 11px;
  background: #15130f; border: 1px solid var(--gold); border-radius: 4px;
  color: var(--ink); font-size: 11px; line-height: 1.5;
  box-shadow: 0 8px 24px rgba(0,0,0,.6);
}
.help:hover .tip { display: block; }
.seat-info .tip { left: auto; right: 0; }

.modcard {
  border: 1px solid var(--gold); border-radius: 4px;
  padding: 10px 12px; margin: 12px 0;
  background: rgba(201,162,39,.07);
}
.modcard b { color: var(--gold); }
.modcard p { margin: 4px 0 0; }
.seat .seat-name { flex: 1; min-width: 0; }
.swatch {
  width: 26px; height: 26px; border-radius: 3px;
  display: grid; place-items: center; color: #fff; font-size: 13px; flex: none;
}
#startBtn { width: 100%; margin-top: 18px; padding: 12px; font-size: 15px; letter-spacing: 2px; }

.joinrow { display: flex; gap: 8px; margin-top: 10px; }
.joinrow input {
  flex: 1; font: inherit; font-size: 14px; letter-spacing: 4px; text-transform: uppercase;
  background: var(--panel2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 3px; padding: 8px 10px;
}
#onCreate { width: 100%; }
.err { margin-top: 10px; padding: 8px 10px; border-left: 2px solid var(--bad); color: var(--bad); font-size: 12px; }
/* The lobby is a panel in the menu's left column. The code sits under the
   lobby name rather than beside it, labelled so it is obviously the thing you
   pass to other players. */
#lobbyTitle { font-size: 24px; letter-spacing: 3px; color: var(--ink); margin: 0 0 12px; }
#lobby .opts { margin: 14px 0 10px; }
/* The lobby code: a framed box, centred in the column, with its label small
   above it. Clicking anywhere on it copies the code; the label says so. */
.codebox {
  display: grid; justify-items: center; gap: 2px;
  margin: 0 auto 14px; padding: 6px 14px 8px;
  background: transparent; border: 1px solid var(--line); border-radius: 3px;
  cursor: copy; color: inherit;
  transition: border-color .15s, background .15s;
}
.codebox:hover:not(:disabled) { border-color: var(--gold); background: rgba(201, 162, 39, .06); color: inherit; }
.codebox.copied { border-color: var(--good); }
.codebox.copied .codelabel { color: var(--good); }
.codelabel {
  font-size: 10px; letter-spacing: 3px; text-transform: uppercase;
  color: var(--dim);
  padding-left: 3px;                      /* balance the trailing letter-spacing */
}
/* Letter-spacing leaves a gap after the last letter; the same on the left
   keeps the code optically centred in its box. */
#lobbyCode { font-size: 28px; font-weight: 700; letter-spacing: 10px; padding-left: 10px; color: var(--gold); line-height: 1.15; }

.lobbylist { margin-top: 10px; border-top: 1px solid var(--line); }
.lrow {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 13px;
}
.lrow .lname { flex: 1; font-weight: 600; }
#lobbySeats .botname {
  flex: 1; min-width: 0; font: inherit; font-size: 12px; padding: 3px 6px;
  background: var(--panel2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 3px;
}
#onNotice { border-left-color: var(--gold); color: var(--gold); }
#lobbySeats .seat { padding: 5px 0; border-bottom: 1px solid var(--line); }
#lobbySeats .botrole { font-size: 12px; padding: 3px 6px; }
#lobbySeats .kick { color: var(--bad); border-color: var(--line); padding: 2px 7px; }
#lobbySeats .kick:hover { border-color: var(--bad); background: var(--bad); color: #fff; }
#lobbySeats .pname { flex: 1; }
#lobbySeats i { color: var(--dim); font-style: normal; font-size: 11px; }
.roleinfo { font-size: 12px; color: var(--dim); border-left: 2px solid var(--line);
  padding-left: 12px; margin: 4px 0 16px; }
.roleinfo b { color: var(--gold); }
#lobbyBot, #lobbyStart { width: 100%; margin-top: 8px; }

/* ------------------------------------------------------------------- game */

header {
  display: flex; align-items: center; gap: 18px;
  padding: 10px 18px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}
.brand { font-size: 22px; letter-spacing: 6px; color: var(--gold); }
.brand span { font-size: 11px; letter-spacing: 2px; color: var(--dim); }
#turnLabel { flex: 1; }
/* A near-black role colour vanishes against the header; give it a halo. */
#turnLabel .whose.onblack {
  text-shadow: 0 0 3px rgba(232, 226, 214, .95), 0 0 1px rgba(232, 226, 214, .9);
}
.hbtns { display: flex; gap: 8px; }

/* Online: every player's connection to the host, in the header. */
#netbar { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; max-width: 52%; }
#netbar.hidden { display: none; }
.nb {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border: 1px solid var(--line); border-radius: 10px;
  font-size: 11px; white-space: nowrap; background: var(--panel2);
}
.nb b { font-weight: 600; max-width: 90px; overflow: hidden; text-overflow: ellipsis; }
.nb b.onblack { text-shadow: 0 0 3px rgba(232, 226, 214, .95); }
.nb em { font-style: normal; color: var(--dim); }
.nb i, .link i { width: 7px; height: 7px; border-radius: 50%; background: var(--dim); flex: none; }
.nb.good i, .link.good i { background: var(--good); }
.nb.fair i, .link.fair i { background: var(--gold); }
.nb.slow i, .link.slow i { background: var(--mefo); }
.nb.down i, .link.down i { background: var(--bad); }
.nb.host i, .link.host i { background: var(--pol); }
.nb.gone, .link.gone { opacity: .55; }
.nb.down em, .link.down { color: var(--bad); }

/* The same, on a lobby seat. */
.link { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--dim); white-space: nowrap; }
#lobbyLeave { margin-top: 10px; width: 100%; }

@media (max-width: 900px) {
  header { flex-wrap: wrap; }
  #netbar { max-width: none; order: 3; width: 100%; justify-content: flex-start; }
}

/* The turn controls, along the bottom of the screen. Its height is declared
   once, as --turnbar, because three things depend on it: the bar itself, how
   much room is left for the board, and how far up the chat has to sit to clear
   it. Change it here and the rest follows. */
#turnbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  height: var(--turnbar);
  display: flex; align-items: center; justify-content: center; gap: 12px;
  background: var(--panel);
  border-top: 1px solid var(--line);
}
#turnbar button { padding: 9px 30px; font-size: 14px; min-width: 130px; }

/* The dice sit in a fixed-width slot, right-aligned against Roll, and the
   pseudo-element balances it on the other side. Without both, the buttons
   would jump sideways the moment a roll appeared or a one-die restriction
   dropped the pair to a single die. */
#turnbar #dice { min-width: 76px; justify-content: flex-end; }
#turnbar::after { content: ''; min-width: 76px; }
#dice { display: flex; gap: 4px; }
.die { font-size: 30px; line-height: 1; color: var(--gold); }
/* Sits directly above the turn bar and rises out from behind it, so the reason
   Roll and End turn are greyed appears next to them rather than at the far end
   of the screen. z-index is deliberately BELOW the bar: the badge starts tucked
   underneath it and slides clear, which is what makes it read as emerging
   rather than fading in on top. */
#syncBadge {
  position: fixed; left: 50%; bottom: var(--turnbar); z-index: 39;
  display: flex; align-items: center; gap: 7px;
  font-size: 11px; letter-spacing: 1px; color: var(--gold);
  padding: 5px 12px;
  background: var(--panel);
  border: 1px solid var(--line); border-radius: 3px;
  white-space: nowrap;
  pointer-events: none;          /* never in the way of the buttons below it */
  transform: translate(-50%, 100%);
  opacity: 0;
  transition: transform .3s cubic-bezier(.16, .84, .44, 1),
              opacity .22s ease-out;
}
#syncBadge.on { transform: translate(-50%, -10px); opacity: 1; }
#syncBadge::before {
  content: ''; width: 8px; height: 8px; border-radius: 50%;
  background: var(--gold); animation: syncPulse 1s ease-in-out infinite;
}
@keyframes syncPulse { 0%,100% { opacity: .25; } 50% { opacity: 1; } }

.dbl {
  font-size: 10px; letter-spacing: 2px; padding: 2px 6px; border-radius: 2px;
  background: var(--good); color: #10140e; font-weight: 700;
}

/* Record on the left at full height, board centre, holdings and turn order
   on the right. */
main {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr) 340px;
  gap: 16px;
  padding: 16px;
  align-items: start;
  height: calc(100vh - 57px - var(--turnbar));
}

/* ------------------------------------------------------------------ board */

/* The viewport the board is panned, zoomed and turned inside. It clips, and
   supplies the perspective; the board itself carries the camera transform,
   which ui.js writes from the view state (the value below is the default). */
#boardView {
  position: relative;
  height: 100%;
  min-height: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  perspective: 1800px;
  cursor: grab;
  touch-action: none;
  user-select: none;
  border-radius: 6px;
}
#boardView.dragging { cursor: grabbing; }

#board {
  display: grid;
  grid-template-columns: 1.35fr repeat(9, 1fr) 1.35fr;
  grid-template-rows: 1.35fr repeat(9, 1fr) 1.35fr;
  gap: 2px;
  aspect-ratio: 1;
  /* Keep the whole ring on screen: square, and never taller than its column. */
  width: 100%;
  max-height: 100%;
  max-width: min(100%, calc(100vh - 90px - var(--turnbar)));
  margin: 0 auto;
  background: var(--line);
  border: 2px solid var(--line);
  border-radius: 4px;
  /* The table seen from a seat: tipped away from the viewer. preserve-3d has
     to run unbroken from here down to each cube face, which is why nothing in
     between may set overflow, opacity < 1 or a filter — any of those flattens
     the pieces back into the board. */
  position: relative;
  transform-style: preserve-3d;
  transform: rotateX(24deg);
  transform-origin: 50% 50%;
  box-shadow: 0 0 50px 8px rgba(0, 0, 0, .55);
}
/* Everything on the board is lifted a hair off its surface. Lying exactly in
   the board's own plane, the cells z-fight with its background, and the far
   row — where depth precision is worst — could vanish outright. */
#board > .cell, #board > .center { transform: translateZ(1px); }
/* The same again for the text on them: a cell is a 3D layer, so its text is a
   layer of its own lying in the cell's plane, and fights it the same way. */
.center { transform-style: preserve-3d; }
.cell-title, .cell-sub, .center > * { transform: translateZ(0.6px); }

/* The board's four edges, so it reads as a slab from any side it is turned to. */
.edge { position: absolute; background: linear-gradient(#2a251e, #16130f); pointer-events: none; }
.edge.s { left: -2px; right: -2px; top: calc(100% + 2px); height: 14px;
          transform-origin: top; transform: rotateX(-90deg); }
.edge.n { left: -2px; right: -2px; bottom: calc(100% + 2px); height: 14px;
          transform-origin: bottom; transform: rotateX(90deg); }
.edge.e { top: -2px; bottom: -2px; left: calc(100% + 2px); width: 14px;
          transform-origin: left; transform: rotateY(90deg); }
.edge.w { top: -2px; bottom: -2px; right: calc(100% + 2px); width: 14px;
          transform-origin: right; transform: rotateY(-90deg); }

.viewctl {
  position: absolute; top: 8px; right: 8px; z-index: 5;
  display: flex; gap: 4px;
}
.viewctl button { padding: 3px 9px; font-size: 13px; min-width: 30px; opacity: .8; }
.viewctl button:hover { opacity: 1; }
.viewhint {
  position: absolute; left: 10px; bottom: 8px; z-index: 5;
  font-size: 10px; letter-spacing: .5px; color: var(--dim); pointer-events: none;
  transition: opacity .6s;
}
#boardView.learned .viewhint { opacity: 0; }

/* The dice, thrown across the board. Same face geometry as the pieces; ivory
   with dark pips, so they cannot be mistaken for anybody's piece. */
#diceLayer {
  position: absolute; left: 0; top: 0; width: 0; height: 0;
  transform-style: preserve-3d; pointer-events: none;
}
.die3d {
  --h: calc(var(--s) / 2);
  position: absolute; left: 0; top: 0;
  width: var(--s); height: var(--s);
  margin: calc(var(--s) / -2) 0 0 calc(var(--s) / -2);
  transform-style: preserve-3d;
}
.die3d .f {
  position: absolute; inset: 0;
  display: grid; grid-template: repeat(3, 1fr) / repeat(3, 1fr);
  padding: 14%; box-sizing: border-box;
  background: #efe9dc;
  border: 1px solid rgba(0, 0, 0, .3);
  border-radius: 14%;
  backface-visibility: hidden;
}
/* 2D mode: the original board. No camera, no slab edges, no depth — and the
   pieces go back to flat squares, which is just the cube's top face with the
   other five hidden. */
.flat #boardView { perspective: none; cursor: default; touch-action: auto; user-select: auto; }
.flat .viewctl, .flat .viewhint, .flat .edge { display: none; }
.flat #board { transform: none; box-shadow: none; transform-style: flat; }
.flat #board > .cell, .flat #board > .center,
.flat .cell-title, .flat .cell-sub, .flat .center > * { transform: none; }
.flat .tokens { gap: 2px; margin-top: 2px; }
.flat .token { --s: 14px; }
.flat .token.active::before { display: none; }
.flat .cube { transform: none; animation: none !important; }
.flat .cube .f { display: none; }
.flat .cube .top {
  display: grid; transform: none; filter: none;
  border: 0; border-radius: 2px; font-size: 9px; text-shadow: none;
}
.flat .token.rim .top { box-shadow: 0 0 0 1px var(--dim) inset; }

.die3d .pip { width: 70%; height: 70%; place-self: center; border-radius: 50%; background: #1a1815; }

.cell {
  position: relative;
  background: var(--panel);
  padding: 4px 3px 3px;
  display: flex;
  flex-direction: column;
  min-width: 0;
  transform-style: preserve-3d;
}
/* The cell cannot clip (it would flatten the pieces), so the text clips itself. */
.cell-title, .cell-sub { overflow: hidden; min-width: 0; }
.cell.prop { cursor: pointer; }
.cell.prop:hover { background: var(--panel2); }
.cell.here { outline: 2px solid var(--gold); outline-offset: -2px; }
.cell.corner { background: #272319; }

.stripe { height: 5px; margin: -4px -3px 4px; background: var(--line); }
.stripe.t-military  { background: var(--mil); }
.stripe.t-civilian  { background: var(--civ); }
.stripe.t-infra     { background: var(--inf); }
.stripe.t-political { background: var(--plt); }
.stripe.t-landmark  { background: var(--lmk); }
.stripe.k-go, .stripe.k-rally { background: var(--good); }
.stripe.k-tax, .stripe.k-gotodeten, .stripe.k-detention { background: var(--bad); }
.stripe.k-bank { background: var(--mefo); }
.stripe.k-event { background: var(--gold); }

.cell-title { font-size: 10px; line-height: 1.15; font-weight: 600; }
.cell-sub { font-size: 9px; color: var(--dim); margin-top: auto; }
.cell-sub s, .ptab s { opacity: 0.6; }
.cell-sub .better, .ptab .better { color: var(--good); font-weight: 600; }
.cell-sub .worse, .ptab .worse { color: var(--bad); font-weight: 600; }
.tokens {
  display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px;
  transform-style: preserve-3d;
}

/* Playing pieces: a cube standing on the board with the role symbol on every
   face. Faces are positioned from the cube's centre, which is lifted half a
   side off the board so the bottom face rests on it. Side faces are stood up
   with rotateX(-90deg) first, then turned about the vertical, so the symbol on
   each one stays upright. */
.token {
  --s: 18px;
  --h: calc(var(--s) / 2);
  width: var(--s); height: var(--s);
  transform-style: preserve-3d;
  flex: none;
}
.cube {
  position: relative; display: block;
  width: 100%; height: 100%;
  transform-style: preserve-3d;
  transform: translateZ(var(--h)) rotateZ(-28deg);
}
.token.active .cube { animation: cubeSpin 6s linear infinite; }
@keyframes cubeSpin {
  from { transform: translateZ(var(--h)) rotateZ(-28deg); }
  to   { transform: translateZ(var(--h)) rotateZ(332deg); }
}
.token.active::before {
  /* A soft ring on the board under the piece whose turn it is. */
  content: ''; position: absolute; inset: -3px; border-radius: 50%;
  background: radial-gradient(closest-side, rgba(201, 162, 39, .55), transparent);
}
.token { position: relative; }

.cube .f {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-style: normal; font-size: 12px; line-height: 1; color: #fff;
  background: var(--c);
  border: 1px solid rgba(0, 0, 0, .35);
  box-sizing: border-box;
  backface-visibility: hidden;
  text-shadow: 0 0 2px rgba(0, 0, 0, .5);
}
:is(.cube, .die3d) > .top    { transform: translateZ(var(--h)); filter: brightness(1.18); }
:is(.cube, .die3d) > .bottom { transform: rotateX(180deg) translateZ(var(--h)); }
:is(.cube, .die3d) > .front  { transform: rotateZ(0deg)   rotateX(-90deg) translateZ(var(--h)); filter: brightness(.95); }
:is(.cube, .die3d) > .right  { transform: rotateZ(-90deg) rotateX(-90deg) translateZ(var(--h)); filter: brightness(.78); }
:is(.cube, .die3d) > .back   { transform: rotateZ(180deg) rotateX(-90deg) translateZ(var(--h)); filter: brightness(.7); }
:is(.cube, .die3d) > .left   { transform: rotateZ(90deg)  rotateX(-90deg) translateZ(var(--h)); filter: brightness(.85); }
/* The black Strasserist piece needs light edges to read on a dark board. */
.token.rim .f { border-color: var(--dim); }

@media (prefers-reduced-motion: reduce) {
  .token.active .cube { animation: none; }
}
.ownbar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; }
.ownbar.shown { box-shadow: 0 0 0 1px rgba(0,0,0,.4) inset; }

.center {
  background: #1f1c17;
  display: grid;
  place-content: center;
  text-align: center;
  padding: 20px;
  gap: 4px;
}
.c-title { font-size: 40px; letter-spacing: 14px; color: var(--gold); }
.c-sub { font-size: 11px; letter-spacing: 3px; color: var(--dim); margin-bottom: 10px; }
.c-year { font-size: 56px; font-weight: 700; line-height: 1; }
.c-grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: 8px 24px; margin: 16px 0; font-size: 12px;
}
.c-grid span { display: block; color: var(--dim); font-size: 10px; letter-spacing: 1px; text-transform: uppercase; }
.c-grid b { font-size: 15px; }
.c-grid .rate { color: var(--mefo); }
.c-grid .osub { display: block; font-style: normal; font-size: 10px; color: var(--dim); }
.c-grid .wide { grid-column: 1 / -1; }
.c-grid .lead { font-size: 11px; line-height: 1.6; color: var(--ink); }
.c-grid .otitle { font-weight: 700; }
/* Black on a dark board is unreadable, so the Strasserist title gets a rim. */
.c-grid .otitle.onblack {
  -webkit-text-stroke: 0.6px #e8e2d6;
  text-shadow: 0 0 3px #e8e2d6, 0 0 1px #e8e2d6;
}
#soundBtn, #viewModeBtn { min-width: 38px; }
#soundBtn.off { color: var(--dim); }
.c-hist {
  font-size: 11px; color: var(--dim); max-width: 320px;
  border-top: 1px solid var(--line); padding-top: 10px;
}
.c-hist b { color: var(--ink); }

/* ------------------------------------------------------------------ aside */

aside {
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  min-height: 0;
  overflow-y: auto;
}

.pcard {
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 3px solid var(--pc);
  border-radius: 4px;
  padding: 8px 10px;
  margin-bottom: 8px;
}
.pcard.active { background: var(--panel2); border-color: var(--gold); }
.pcard.dead { opacity: .4; }
.ptop { display: flex; align-items: center; gap: 6px; }
.pname { font-weight: 700; }
.prole { font-size: 11px; color: var(--dim); margin: 1px 0 6px; }
.badge {
  font-size: 9px; letter-spacing: 1px; text-transform: uppercase;
  padding: 1px 5px; border-radius: 2px; background: var(--line);
}
.badge.f { background: var(--gold); color: #1a1815; }
.badge.d, .badge.x { background: var(--bad); color: #fff; }
.badge.o { background: var(--plt); color: #fff; }
.badge.off { background: transparent; color: var(--bad); box-shadow: 0 0 0 1px var(--bad) inset; }
#lobbySeats .offline {
  font-size: 9px; letter-spacing: 1px; text-transform: uppercase;
  color: var(--bad); box-shadow: 0 0 0 1px var(--bad) inset;
  border-radius: 2px; padding: 1px 5px;
}

.pstats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; }
.pstats span { display: block; font-size: 9px; color: var(--dim); text-transform: uppercase; letter-spacing: .5px; }
.pstats b { font-size: 14px; }
.pstats .mefo { color: var(--mefo); }
.pstats .pol { color: var(--pol); }
.pinc {
  font-size: 10px; color: var(--dim); margin-top: 5px;
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
}
/* Pushed right on its own, so it stays right-aligned even when the income
   figures are long enough to wrap it onto a line of its own. */
.pbail { margin-left: auto; white-space: nowrap; }
.pbail.b1 { color: var(--gold); }
.pbail.b0 { color: var(--bad); font-weight: 700; }

#holdings {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 10px;
}
#holdings h3, .logbox h3 {
  margin: 0 0 8px; font-size: 12px; letter-spacing: 2px;
  text-transform: uppercase; color: var(--dim);
}
.hrow { display: grid; gap: 5px; margin-bottom: 8px; }
.mefonote {
  font-size: 11px; color: var(--dim); line-height: 1.5;
  border-left: 2px solid var(--mefo); padding-left: 9px; margin-bottom: 9px;
}
.mefonote b { color: var(--mefo); }
.hold {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 2px 8px;
  padding: 5px 0;
  border-top: 1px solid var(--line);
  font-size: 12px;
}
.hname { font-weight: 600; }
.hmod { grid-column: 1; font-size: 10px; color: var(--mefo); cursor: help; }
.hinc { grid-column: 1; font-size: 10px; color: var(--dim); }
.hold .sell { grid-column: 2; grid-row: 1 / 4; align-self: center; }

.tag {
  display: inline-block; font-size: 9px; padding: 1px 5px; border-radius: 2px;
  background: var(--line); color: #fff; letter-spacing: 1px;
}
.tag.t-military  { background: var(--mil); }
.tag.t-civilian  { background: var(--civ); }
.tag.t-infra     { background: var(--inf); }
.tag.t-political { background: var(--plt); }
.tag.t-landmark  { background: var(--lmk); }

.logbox {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 10px;
  /* Full column height, with only the entries scrolling. */
  height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
#log { flex: 1; overflow-y: auto; font-size: 12px; min-height: 0; }
.l { padding: 2px 0; border-bottom: 1px solid rgba(61,55,47,.5); }
.ly { color: var(--dim); font-size: 10px; margin-right: 4px; }
.l.good { color: var(--good); }
.l.bad  { color: var(--bad); }
.l.mefo { color: var(--mefo); }
.l.pol  { color: var(--pol); }
.l.warn { color: var(--gold); }
.l.card { color: #b9a9d6; }
.l.muted { color: var(--dim); }
.l.year { color: var(--gold); font-size: 15px; letter-spacing: 4px; padding: 6px 0; }
.l.hist { color: var(--gold); background: rgba(201,162,39,.08); padding: 6px; }

/* ------------------------------------------------------------------ modal */

/* ---------------------------------------------------------------- splashes */

#splashes {
  position: fixed; top: 64px; left: 50%; transform: translateX(-50%);
  z-index: 70; display: flex; flex-direction: column; gap: 6px;
  align-items: center; pointer-events: none; width: min(560px, 92vw);
}
.splash {
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 8px 12px;
  background: #201d18; border: 1px solid var(--line);
  border-left: 3px solid var(--dim); border-radius: 4px;
  box-shadow: 0 8px 24px rgba(0,0,0,.55);
  font-size: 13px;
  animation: splashIn .22s ease-out;
}
.splash.out { opacity: 0; transform: translateY(-8px); transition: all .4s ease-in; }
@keyframes splashIn { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: none; } }

.splash.mine { border-color: var(--gold); background: #2a2418; }
.splash .schips { display: flex; gap: 3px; flex: none; }
.splash .chip {
  width: 17px; height: 17px; border-radius: 3px;
  display: grid; place-items: center; font-size: 10px; color: #fff;
}
.splash .stext { flex: 1; min-width: 0; }
.splash .samt { font-weight: 700; flex: none; }
.splash .samt.up { color: var(--good); }
.splash .samt.down { color: var(--bad); }
.splash .samt.flat { color: var(--civ); }

.splash.k-good     { border-left-color: var(--good); }
.splash.k-bad      { border-left-color: var(--bad); }
.splash.k-warn     { border-left-color: var(--gold); }
.splash.k-transfer { border-left-color: var(--civ); }
.splash.k-buy      { border-left-color: var(--mil); }
.splash.k-pol      { border-left-color: var(--pol); }
.splash.k-hist     { border-left-color: var(--gold); background: #2a2418; letter-spacing: 1px; }

@media (max-width: 1240px) { #splashes { top: 56px; } }

/* -------------------------------------------------------------------- chat */

#chat {
  /* Sits on top of the turn bar rather than under it. */
  position: fixed; right: 16px; bottom: var(--turnbar); z-index: 55;
  width: 300px; max-width: calc(100vw - 32px);
  display: flex; flex-direction: column;
  font-size: 12px;
}
#chatBar {
  display: flex; align-items: center; gap: 8px;
  width: 100%; padding: 8px 12px;
  background: var(--panel); border: 1px solid var(--line); border-bottom: none;
  border-radius: 4px 4px 0 0;
  color: var(--ink); font: inherit; font-size: 12px; letter-spacing: 2px;
  text-transform: uppercase; cursor: pointer;
}
#chatBar:hover { border-color: var(--gold); color: #fff; }
#chatBar span:first-child { flex: 1; text-align: left; }
#chatCaret { color: var(--dim); transition: transform .25s ease; }
#chat.collapsed #chatCaret { transform: rotate(180deg); }
#chatUnread {
  background: var(--gold); color: #1a1815; font-weight: 700;
  border-radius: 9px; padding: 1px 7px; letter-spacing: 0;
}

/* The slide: the body's height animates, so the panel rises out of the bar. */
#chatBody {
  background: var(--panel); border: 1px solid var(--line); border-bottom: none;
  max-height: 320px; opacity: 1;
  transition: max-height .28s ease, opacity .18s ease;
  overflow: hidden;
}
#chat.collapsed #chatBody { max-height: 0; opacity: 0; }

/* The message area alone gets its own ground; the bar and the input row stay
   in the board's palette. */
/* The message area is light parchment: soft to read, and every role colour —
   from the Strasserist's black to the Socialist's bright red — stands out on
   it. The old mid-grey sat between the two ends, so the dark names sank into
   it. Names that would still be too faint are deepened in online.js. */
#chatLog {
  height: 250px; overflow-y: auto; padding: 10px 12px;
  display: flex; flex-direction: column; gap: 6px;
  background: #ece5d6; color: #2b2620;
}
#chatLog .cmsg { line-height: 1.45; word-break: break-word; }
#chatLog .cwho { font-weight: 700; margin-right: 4px; }
#chatLog .csys { color: #6b6458; font-style: italic; }
#chatLog .cempty { color: #6b6458; }

#chatForm { display: flex; gap: 6px; padding: 8px; border-top: 1px solid var(--line); }
#chatInput {
  flex: 1; min-width: 0; font: inherit; font-size: 12px; padding: 6px 8px;
  background: var(--panel2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 3px;
}
#chatInput:focus { outline: none; border-color: var(--gold); }

@media (max-width: 1240px) { #chat { right: 8px; width: 260px; } }

/* In the lobby the chat is a full-height column down the right of the screen,
   always open, and the menu makes room for it. On a narrow screen it stays the
   corner panel — a column would leave nothing for the lobby itself. */
@media (min-width: 901px) {
  body.in-lobby { --lobbychat: 320px; }
  body.in-lobby .menu { padding-right: var(--lobbychat); }
  body.in-lobby #chat {
    top: 0; bottom: 0; right: 0; width: var(--lobbychat); max-width: none;
    border-left: 2px solid var(--line);
  }
  body.in-lobby #chatBar { display: none; }
  body.in-lobby #chatBody,
  body.in-lobby #chat.collapsed #chatBody {
    flex: 1; max-height: none; opacity: 1; border: 0;
    display: flex; flex-direction: column;
  }
  body.in-lobby #chatLog { flex: 1; height: auto; }
}

/* ------------------------------------------------------------- victory card */

#victory {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(0,0,0,.82);
  display: grid; place-items: center; padding: 20px;
  animation: vFade .5s ease-out;
}
@keyframes vFade { from { opacity: 0; } to { opacity: 1; } }

.vcard {
  text-align: center; max-width: 520px; width: 100%;
  background: var(--panel);
  border: 2px solid var(--wc);
  border-radius: 6px;
  padding: 40px 36px 32px;
  box-shadow: 0 0 90px rgba(0,0,0,.8), 0 0 40px -10px var(--wc);
  animation: vRise .55s cubic-bezier(.2,.9,.3,1.2);
}
@keyframes vRise { from { transform: translateY(22px) scale(.94); opacity: 0; } to { transform: none; opacity: 1; } }

.vkicker {
  font-size: 11px; letter-spacing: 4px; text-transform: uppercase;
  color: var(--dim); margin-bottom: 18px;
}
.vpiece {
  width: 62px; height: 62px; border-radius: 6px; margin: 0 auto 16px;
  display: grid; place-items: center; font-size: 30px; color: #fff;
}
.vname { margin: 0; font-size: 42px; line-height: 1.1; color: var(--wc); }
.vname.onblack { -webkit-text-stroke: 1px #e8e2d6; text-shadow: 0 0 12px rgba(232,226,214,.45); }
.vcard .vrole { font-size: 13px; color: var(--dim); margin-top: 6px; letter-spacing: 1px; }
.vstats {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
  margin: 26px 0 24px; padding-top: 20px; border-top: 1px solid var(--line);
}
.vstats span { display: block; font-size: 9px; letter-spacing: 1px; text-transform: uppercase; color: var(--dim); }
.vstats b { font-size: 17px; }
.vbtns { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.vbtns button { padding: 11px; letter-spacing: 2px; }

button.danger { border-color: var(--bad); color: var(--bad); }
button.danger:hover:not(:disabled) { background: var(--bad); color: #fff; border-color: var(--bad); }
.cbtns button.danger:first-child { background: var(--panel2); color: var(--bad); }

/* The black piece needs its rim here too. */
.vpiece.rim { box-shadow: 0 0 0 1px var(--dim) inset; }

#modal, #rules, #info, #browser, #confirm {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(0,0,0,.65);
  display: grid; place-items: center;
  padding: 20px;
}
#info { z-index: 60; }      /* a lookup can sit over a pending decision */
#browser { z-index: 65; }
#confirm { z-index: 95; }   /* above everything, including the victory card */
#modal .card, #info .card, #browser .card, #confirm .card, .rulecard {
  background: var(--panel);
  border: 1px solid var(--gold);
  border-radius: 5px;
  padding: 24px;
  max-width: 460px;
  width: 100%;
  box-shadow: 0 20px 60px rgba(0,0,0,.6);
}
#modal h2, #info h2, #browser h2, #confirm h2 { margin: 0 0 12px; color: var(--gold); font-size: 20px; }
#browser .card, #confirm .card { box-shadow: 0 24px 70px rgba(0,0,0,.75); }
#confirm .card { max-width: 400px; }
#browser #onList { max-height: 46vh; overflow-y: auto; }
.browsefoot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-top: 14px; font-size: 11px;
}
.cbody { margin-bottom: 18px; }
.cbtns { display: grid; gap: 7px; }

/* The forced sale. The debt is the loudest thing on the card, because it is the
   only number that decides when the player is allowed to stop selling. There is
   deliberately no dismiss button: the engine only raises this when selling CAN
   clear the debt, so there is always a way out, and closing it would leave a
   creditor unpaid mid-turn. */
.settle .owed {
  font-size: 26px; font-weight: 700; color: var(--bad);
  letter-spacing: 1px; margin: 0 0 14px;
  border-bottom: 1px solid var(--line); padding-bottom: 10px;
}
.settle .owed span {
  display: block; font-size: 11px; font-weight: 400; letter-spacing: 0;
  color: var(--dim); margin-top: 3px;
}
/* A player can own the whole board; the list scrolls rather than the card. */
.settle .sellwrap { max-height: 52vh; overflow-y: auto; margin-bottom: 0; }
.settle .hold:first-child { border-top: 0; }
.cbtns button:first-child { background: var(--gold); color: #1a1815; border-color: var(--gold); font-weight: 700; }

.ptab { width: 100%; margin-top: 10px; border-collapse: collapse; font-size: 13px; }
.ptab td { padding: 4px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.ptab td:first-child { color: var(--dim); width: 40%; }

.rulecard { max-width: 680px; max-height: 86vh; overflow-y: auto; position: relative; }
.rulecard .news { padding-right: 28px; }   /* clear of the close button */
.rulecard h2 { color: var(--gold); margin-top: 0; letter-spacing: 3px; }
.rulecard h3 { color: var(--gold); font-size: 13px; letter-spacing: 2px; text-transform: uppercase; margin: 20px 0 4px; }
.rulecard p { margin: 0; color: var(--dim); }
.rulecard b { color: var(--ink); }
.closeX { position: absolute; top: 12px; right: 12px; padding: 2px 9px; font-size: 18px; }

@media (max-width: 1240px) {
  /* Stacked, so the page scrolls — and a fixed bar would otherwise sit on top
     of whatever the last panel is. Leave it room to scroll clear. */
  main { grid-template-columns: 1fr; height: auto;
         padding-bottom: calc(16px + var(--turnbar)); }
  aside, .logbox { height: auto; overflow: visible; }
  #log { max-height: 300px; }
  #board { max-width: 640px; }
  #boardView { height: auto; padding: 24px 0; }
}
