/* Miryou War — join/play client. Same palette + type as the spectator page. */
:root {
  --bg: #0b0e14; --panel: #131824; --line: #232a3a;
  --text: #d7deea; --dim: #7b8598;
  --blue: #4d9fff; --red: #ff5d5d; --gold: #e8c35a; --green: #45d06c;
  /* v2.6: the Source is its own colour so nothing else on screen can be mistaken
     for it, and "unstable ruin" owns amber the way "hazard" already owns yellow. */
  --boss: #c774ff; --amber: #ffa53a;
}
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* `100%` first as the floor, then `100dvh`: iOS Safari inflates `100vh` by the
   height of the URL bar, which pushed the bottom row (now the CONTROL PAD) off the
   screen. `dvh` tracks the bar as it hides and shows; a browser without it keeps
   the old `100%` behaviour. */
html, body { height: 100%; height: 100dvh; }
body {
  background: var(--bg); color: var(--text);
  font: 15px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif;
  overflow: hidden;
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}
button { font: inherit; color: inherit; cursor: pointer; }

.view { display: none; height: 100%; }
.view.show { display: flex; }

/* ------------------------------- join screen ------------------------------ */
#join-view { align-items: center; justify-content: center; padding: 16px; }
.join-card {
  width: 100%; max-width: 420px; background: var(--panel);
  border: 1px solid var(--line); border-radius: 12px; padding: 22px 18px 18px;
  display: flex; flex-direction: column; gap: 10px;
}
.join-card h1 { font-size: 22px; letter-spacing: 0.06em; color: var(--gold); }
.join-card .sub { color: var(--dim); font-size: 13px; margin-bottom: 6px; }
.join-card label { font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--dim); }
.name-row { display: flex; gap: 8px; }
input {
  flex: 1; min-width: 0; background: #0d1119; color: var(--text);
  border: 1px solid var(--line); border-radius: 8px; padding: 11px 12px;
  font: inherit; outline: none;
}
input:focus { border-color: var(--blue); }
button.primary {
  background: linear-gradient(#3f86e6, #2f6cc0); border: 0; border-radius: 8px;
  padding: 14px; font-weight: 700; letter-spacing: 0.06em; color: #fff;
}
button.primary:disabled { background: #2a3245; color: var(--dim); cursor: default; }
button.ghost {
  background: #182032; border: 1px solid var(--line); border-radius: 8px;
  padding: 11px 14px; color: var(--text);
}
button.ghost.wide { width: 100%; }
.join-status { font-size: 12px; color: var(--dim); min-height: 18px; text-align: center; }
.join-status.err { color: var(--red); }
.join-status.ok { color: var(--green); }
.tip { font-size: 12px; color: var(--dim); text-align: center; }
.tip a { color: var(--blue); text-decoration: none; }
.t-gold { color: var(--gold); } .t-boss { color: var(--boss); } .t-bad { color: var(--red); }

/* -------------------------------- game view ------------------------------- */
#game-view { flex-direction: column; gap: 6px; padding: 6px 8px 8px;
  width: 100%; max-width: 560px; margin: 0 auto; }
#game-view > * { flex-shrink: 0; }

#topbar { display: flex; align-items: center; gap: 8px; font-size: 12px; }
#topbar .brand { font-weight: 700; letter-spacing: 0.08em; }
#topbar .war-no { color: var(--gold); }
#topbar .spacer { flex: 1; }
#topbar .me-name { color: var(--dim); max-width: 40%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
#live-dot { width: 8px; height: 8px; border-radius: 50%; background: #555; }
#live-dot.on { background: var(--green); box-shadow: 0 0 6px var(--green); }

/* v2: day/night badge + "you are in base" chip */
.day-badge { color: var(--dim); border: 1px solid var(--line); border-radius: 9px;
  padding: 0 6px; font-size: 11px; }
.day-badge.night { color: #9db6ea; border-color: #2b3a5c; background: #141c2e; }
.base-chip { color: var(--green); border: 1px solid #2a6a3a; background: #10231a;
  border-radius: 9px; padding: 0 6px; font-size: 10px; font-weight: 700;
  letter-spacing: 0.08em; }

/* v2.9 the DIRECT toggle. Until the player has used it once it pulses, because a
   mode nobody finds is a mode that does not exist — and this one is the whole
   point of the release. */
.dchip { background: #141a28; border: 1px solid var(--line); border-radius: 9px;
  padding: 2px 8px; font-size: 10px; font-weight: 800; letter-spacing: 0.1em;
  color: var(--dim); }
.dchip.on { border-color: var(--gold); color: #12100a;
  background: linear-gradient(#ffdc82, #e0a92c); }
.dchip.hint { border-color: var(--gold); color: var(--gold);
  animation: dpulse 1.4s ease-in-out infinite; }
@keyframes dpulse { 0%,100% { box-shadow: 0 0 0 0 #e8c35a00; }
  50% { box-shadow: 0 0 10px 1px #e8c35a99; } }

#warbar { height: 16px; border-radius: 5px; overflow: hidden; display: flex;
  border: 1px solid var(--line); background: var(--panel); position: relative; }
#warbar .blue { background: linear-gradient(#5aa8ff, #3d86e0); width: 50%; transition: width .6s; }
#warbar .red { background: linear-gradient(#ff6b6b, #d94848); width: 50%; transition: width .6s; }
#warbar span { position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; font-size: 10px; font-weight: 600; color: #fffd;
  text-shadow: 0 1px 2px #0008; }

#mapwrap { position: relative; border: 1px solid var(--line); border-radius: 8px;
  overflow: hidden; background: var(--panel); align-self: center;
  touch-action: none; user-select: none; -webkit-user-select: none;
  transition: border-color .6s; }
/* night: the map frame goes cold (the canvas gets its own tint in JS) */
body.night #mapwrap { border-color: #2b3a5c; }
canvas { display: block; }

#dmg-flash { position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(circle, #ff000000 40%, #ff2a2a66 100%); opacity: 0;
  transition: opacity .35s; }
#dmg-flash.on { opacity: 1; transition: opacity 0s; }

/* ---------------------- v2.6 map overlays (pointer-through) ---------------- */
/* All of these sit ON the canvas. `pointer-events: none` everywhere is load
   bearing: the whole map is one tap target and a HUD strip must never eat a move
   order. Every strip is hidden until the server actually sends its field, so a
   pre-2.6 server renders exactly the old screen. */
#bossbar, #relicbar, #danger, #danger-edge { position: absolute; pointer-events: none; }
/* ...and they get out of the way when YOU are standing behind them. The Source
   strip and the relic strip go nearly transparent — every number on them is
   re-readable a second later. The DANGER banner only half-fades: it is the one
   thing on this screen worth losing a pixel of the map for. */
#bossbar, #relicbar, #danger { transition: opacity .22s; }
body.hudfade #bossbar, body.hudfade #relicbar { opacity: .14; }
body.hudfade #danger { opacity: .72; }

#bossbar { left: 0; right: 0; top: 0; padding: 5px 8px 6px;
  background: linear-gradient(#0b0e14e8, #0b0e1490); }
.bb-top { display: flex; align-items: baseline; gap: 6px; font-size: 10px;
  letter-spacing: 0.1em; }
.bb-name { font-weight: 800; color: var(--boss); text-shadow: 0 0 8px #c774ff66; }
.bb-sp { flex: 1; }
.bb-hp-txt { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: #d8c2ea; letter-spacing: 0; }
.bb-rage { color: #fff; background: #b1122c; border-radius: 4px; padding: 0 5px;
  font-weight: 800; animation: ragepulse .7s ease-in-out infinite; }
@keyframes ragepulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
.bb-bar { height: 9px; margin-top: 3px; border-radius: 3px; overflow: hidden;
  background: #240e33; border: 1px solid #4a1d63; }
.bb-bar .fill { height: 100%; width: 100%;
  background: linear-gradient(#e08bff, #8e2fc4); transition: width .25s; }
/* ARMOUR is a SEPARATE bar on purpose: it is damage the Source shrugs off, not
   health, and a player who reads it as "more hp" learns the wrong lesson. */
.bb-armour { height: 6px; margin-top: 3px; border-radius: 3px; overflow: hidden;
  background: #2a2113; border: 1px solid #5a4520; }
.bb-armour .fill { height: 100%; width: 0;
  background: repeating-linear-gradient(115deg, #ffc061 0 5px, #b7761b 5px 10px);
  transition: width .3s; }
.bb-armour-txt { font-size: 9.5px; letter-spacing: 0.05em; color: var(--amber);
  margin-top: 2px; text-shadow: 0 1px 3px #000a; }
#bossbar.broken .bb-armour { display: none; }
#bossbar.broken .bb-armour-txt { color: var(--green); font-weight: 700; }
#bossbar.dead .bb-bar .fill { background: #3a3f4d; }
#bossbar.dead .bb-name { color: var(--gold); }

#relicbar { left: 0; right: 0; bottom: 0; padding: 4px 8px 5px; display: flex;
  align-items: center; gap: 7px; font-size: 11px;
  background: linear-gradient(#0b0e1400, #0b0e14e0); }
#relicbar .pips { letter-spacing: 1px; font-size: 12px; line-height: 1;
  white-space: nowrap; overflow: hidden; }
#relicbar .pips i { font-style: normal; }
#relicbar .pips i.got { color: var(--gold); text-shadow: 0 0 6px #e8c35a88; }
#relicbar .pips i.out { color: #5d6577; }
#relicbar .pips i.lost { color: #7a4340; }
#relicbar .rtxt { color: var(--dim); flex: 1; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
#relicbar .carry { color: #12100a; background: linear-gradient(#ffdc82, #e0a92c);
  border-radius: 5px; padding: 2px 7px; font-weight: 800; letter-spacing: 0.06em;
  font-size: 10px; white-space: nowrap; box-shadow: 0 0 10px #e8c35a77;
  animation: carrypulse 1.2s ease-in-out infinite; }
@keyframes carrypulse { 0%,100% { box-shadow: 0 0 8px #e8c35a55; }
  50% { box-shadow: 0 0 18px #ffd873cc; } }
#relicbar .carry.banking { background: linear-gradient(#8bf0ab, #35a35a); color: #04150a; }

/* The danger banner. Big, high-contrast, centred over the map: this is the thing
   a player must read in sunlight while running. */
/* Pinned just under the Source strip rather than floated mid-map: the in-world
   collapse countdown and the AoE timer are drawn AT their danger, and a banner
   parked over the middle of the map lands on top of exactly those. */
#danger { left: 0; right: 0; top: 8px; text-align: center; padding: 3px 8px 10px;
  background: linear-gradient(#0b0e14d8 55%, #0b0e1400); }
body.has-source #danger { top: 58px; }
.dg-title { font-size: 25px; font-weight: 900; letter-spacing: 0.08em; line-height: 1.1;
  color: #fff; text-shadow: 0 0 14px #000, 0 2px 0 #000, 0 0 26px currentColor; }
.dg-sub { font-size: 12.5px; font-weight: 700; letter-spacing: 0.08em; color: #ffe0d4;
  text-shadow: 0 0 10px #000, 0 1px 2px #000; }
#danger.lvl-warn .dg-title { color: var(--amber); }
#danger.lvl-crit .dg-title { color: #ff5a4a; animation: dgshake .28s linear infinite; }
@keyframes dgshake { 0%,100% { transform: translateX(0) } 25% { transform: translateX(-3px) }
  75% { transform: translateX(3px) } }

/* Inset frame flash for "you are standing in it". Cheaper and less blinding than
   filling the map, and it survives being looked at for 12 s. */
#danger-edge { inset: 0; box-shadow: inset 0 0 0 0 #0000; opacity: 0;
  transition: opacity .18s; }
#danger-edge.warn { opacity: 1; box-shadow: inset 0 0 22px 3px #ff9a2a77;
  animation: edgepulse .9s ease-in-out infinite; }
#danger-edge.crit { opacity: 1; box-shadow: inset 0 0 30px 5px #ff2f2f99;
  animation: edgepulse .34s ease-in-out infinite; }
@keyframes edgepulse { 0%,100% { opacity: .45; } 50% { opacity: 1; } }

/* Toasts moved to the FOOT of the map: the top of the map is now owned by the
   Source strip and the danger banner, and a toast landing on "MOVE!" is the one
   collision this screen cannot afford. */
#toasts { position: absolute; left: 0; right: 0; bottom: 26px; display: flex;
  flex-direction: column-reverse; align-items: center; gap: 4px; pointer-events: none; }
.toast { background: #0b0e14dd; border: 1px solid var(--line); border-radius: 6px;
  padding: 4px 10px; font-size: 12px; animation: pop 2.4s forwards; }
.toast.bad { border-color: #6a2a2a; color: var(--red); }
.toast.good { border-color: #2a6a3a; color: var(--green); }
@keyframes pop { 0% { opacity: 0; transform: translateY(6px); }
  10%,75% { opacity: 1; transform: none; } 100% { opacity: 0; } }

.overlay { position: absolute; inset: 0; display: none; flex-direction: column;
  align-items: center; justify-content: center; gap: 10px; text-align: center;
  background: #0b0e14dd; padding: 16px; }
.overlay.show { display: flex; }
.overlay .big { font-size: 22px; font-weight: 700; color: var(--gold);
  letter-spacing: 0.05em; }
#dead-overlay .big { color: var(--red); }
#dead-overlay button { min-width: 160px; }

#hud { display: flex; align-items: center; gap: 10px; font-size: 12px; }
.hpwrap { display: flex; align-items: center; gap: 6px; flex: 0 0 45%; }
#hpbar { flex: 1; height: 12px; background: #1b2130; border: 1px solid var(--line);
  border-radius: 4px; overflow: hidden; }
#hpbar .fill { height: 100%; width: 100%; background: linear-gradient(#5ad07a, #35a35a);
  transition: width .25s; }
#hpbar.low .fill { background: linear-gradient(#ff7a5a, #d94848); }
/* in base the site heals you — pulse the bar so it reads at a glance */
#hpbar.healing { border-color: #2a6a3a; }
#hpbar.healing .fill { animation: heal 1.1s ease-in-out infinite; }
@keyframes heal { 0%, 100% { filter: none; } 50% { filter: brightness(1.55); } }
#hp-label { color: var(--dim); min-width: 46px; }
#order-line { flex: 1; color: var(--dim); text-align: right; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
#order-line b { color: var(--text); }
#order-line .st-accepted { color: var(--blue); }
#order-line .st-completed { color: var(--green); }
#order-line .st-rejected, #order-line .st-failed { color: var(--red); }
/* v3.5 gear slots — one quiet line under the order echo; gold because gear is gold */
#gear-line { color: #cdb46a; font-size: 11px; text-align: right; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }

/* ======================= v2.9 DIRECT CONTROL PAD ========================== */
/* Direct mode is a LAYOUT, not an overlay. The feed, the chat box, the order
   buttons and the settings row all go away, which buys ~110 px — spent on a
   BIGGER map and on putting both thumbs at the true bottom edge of the phone
   rather than halfway up it. Nothing here floats over the map: an attack button
   that also sends a move order is the single worst bug this screen could have. */
#pad { display: none; }
body.direct #pad {
  /* LAST in the column, whatever the DOM order says: the buttons belong at the
     bottom edge of the phone, where a thumb rests. The feed keeps its flex:1 and
     absorbs whatever slack a taller screen leaves above them. */
  display: grid; order: 9; margin-top: auto;
  grid-template-columns: 1fr 1fr; grid-template-areas: "chip chip" "stick acts" "hint hint";
  gap: 6px 8px; touch-action: none; user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none;
}
/* What direct mode hides: the ORDER buttons (pressing one would end direct mode
   behind the player's back), the settings row, the order-mode hint line, and the
   chat box. What it KEEPS is the feed — the map is square and therefore capped by
   the viewport WIDTH, so removing the feed too would not buy the map a single
   pixel, it would only leave ~120 px of dead black between the map and the pad. */
body.direct #controls, body.direct #settings, body.direct #tips,
body.direct #chat-form { display: none; }
body.direct #feed { min-height: 34px; }

.stickzone { grid-area: stick; position: relative; height: 168px; border-radius: 16px;
  background: radial-gradient(circle at 50% 55%, #172033 0%, #0f1522 75%);
  border: 1px solid var(--line); overflow: hidden; }
.stickzone.live { border-color: #3d6ea8; }
#stick-ring { position: absolute; width: 108px; height: 108px; left: 50%; top: 55%;
  margin: -54px 0 0 -54px; border: 2px dashed #2c3a52; border-radius: 50%; }
#stick-knob { position: absolute; width: 58px; height: 58px; left: 50%; top: 55%;
  margin: -29px 0 0 -29px; border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, #9cc8ff, #2f6cc0 70%, #21497f);
  box-shadow: 0 3px 12px #000a, inset 0 0 0 1px #ffffff22; }
.stick-cap { position: absolute; left: 0; right: 0; top: 8px; text-align: center;
  font-size: 10px; letter-spacing: 0.16em; color: #4a566d; font-weight: 700; }

#acts { grid-area: acts; height: 168px; display: grid; gap: 8px;
  grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr;
  grid-template-areas: "lock heavy" "roll light"; }
.abtn { border-radius: 50%; aspect-ratio: 1 / 1; justify-self: center;
  align-self: center; width: 100%; max-width: 78px; border: 2px solid var(--line);
  background: #182032; font-size: 11px; font-weight: 800; letter-spacing: 0.06em;
  color: var(--text); touch-action: none; }
.abtn:active, .abtn.down { transform: scale(0.93); filter: brightness(1.5); }
.abtn.light { grid-area: light; border-color: #3d6ea8; color: #b5dcff;
  background: radial-gradient(circle at 35% 30%, #24344e, #131b2b); }
.abtn.heavy { grid-area: heavy; border-color: #8a5320; color: #ffc98a;
  background: radial-gradient(circle at 35% 30%, #33231a, #1d1410); }
.abtn.roll  { grid-area: roll;  border-color: #2a6a3a; color: #8bf0ab;
  background: radial-gradient(circle at 35% 30%, #16281d, #0e1a13); }
.abtn.lock  { grid-area: lock;  border-color: #4a3a6a; color: #cbb0ff;
  background: radial-gradient(circle at 35% 30%, #221a33, #140f1f); font-size: 10px; }
.abtn.lock[aria-pressed="true"] { border-color: var(--boss); color: #12100a;
  background: linear-gradient(#dfaaff, #a24fd8); }
.abtn:disabled { opacity: .35; }
/* v3.0: you cannot pay for this one. NOT `disabled` — the server owns that verdict
   and the pool moves between snapshots, so the button stays pressable and simply
   stops claiming it will work. The ROLL case is the loud one: losing the dodge is
   the only loss in this game you cannot substitute for. */
.abtn.nostam { opacity: .42; border-color: #3a4152; color: #6c7688;
  background: #12172270; }
.abtn.roll.nostam { border-style: dashed; }
/* v3.5 realm spell. Teal like every other rite surface, floated over the pad's
   top-right corner so the 2x2 combat grid keeps its muscle-memory positions. */
#cast-btn { position: absolute; right: 4px; top: -60px; width: 54px; max-width: 54px;
  border-color: #2a6a62; color: #7fe3d4; font-size: 9px;
  background: radial-gradient(circle at 35% 30%, #14312c, #0d1f1c); }
#cast-btn.cooling { opacity: .45; border-style: dashed; }
#acts { position: relative; }

/* The commitment readout. A mis-aimed heavy has to LOOK like a mistake, and the
   0.70 s you cannot move afterwards is the part a player must learn to fear. */
#actchip { grid-area: chip; text-align: center; font-size: 11px; font-weight: 800;
  letter-spacing: 0.14em; border-radius: 6px; padding: 3px 0; min-height: 21px;
  border: 1px solid var(--line); background: #101623; color: var(--dim); }
#actchip.windup { color: #12100a; background: linear-gradient(#ffdc82, #e0a92c);
  border-color: #e0a92c; }
#actchip.active { color: #06121e; background: linear-gradient(#ffffff, #9cc8ff);
  border-color: #fff; }
#actchip.recovery { color: #ffb0a8; background: #2a1418; border-color: #7a3333; }
#actchip.iframe { color: #04150a; background: linear-gradient(#c9ffd9, #45d06c);
  border-color: #8bf0ab; }
#actchip.stagger { color: #12100a; background: linear-gradient(#ffe89a, #d9a83a);
  border-color: #ffe08a; }
/* v3.0 `act_deny`: the press the sim threw away, named, over the thumb. Before the
   field existed a stamina refusal was completely silent on the wire and this chip
   could only sit on READY while the player pressed a button that did nothing. */
#actchip.deny { color: #ffd9d4; background: #3a1216; border-color: #a33a3a;
  animation: denyflash .18s ease-out 2; }
@keyframes denyflash { 0%, 100% { filter: none; } 50% { filter: brightness(1.9); } }
#pad-hint { grid-area: hint; font-size: 10.5px; line-height: 1.25; color: #6c7688;
  text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* stamina (v3.0). Sits beside hp, same grammar, its own colour so it can never be
   misread as health. In DIRECT mode it gets more of the row than the order line,
   because in direct mode the order line says "direct control" and the stamina bar
   says whether you can still dodge. */
#stamwrap { display: flex; align-items: center; gap: 6px; flex: 0 0 28%; }
#stamwrap[hidden] { display: none; }
body.direct #stamwrap { flex: 0 0 40%; }
body.direct .hpwrap { flex: 0 0 40%; }
#stambar { position: relative; flex: 1; height: 10px; background: #1b2130;
  border: 1px solid var(--line); border-radius: 4px; overflow: hidden; }
#stambar .fill { height: 100%; width: 100%;
  background: linear-gradient(#ffe08a, #d9a83a); transition: width .12s linear; }
/* below the ROLL's cost the escape is gone — the one loss with no substitute */
#stambar.spent .fill { background: linear-gradient(#ff9a5a, #c25a2a); }
/* The cost gates. Dark while unaffordable, bright once the pool has cleared them,
   so the bar answers "which of the three can I still pay for" at a glance instead
   of "how full am I", which is the question that never mattered. */
#stambar .gate { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px;
  background: #0b0e14cc; }
#stambar .gate.met { background: #0b0e1466; box-shadow: 1px 0 0 #ffffff55; }
#stambar .g-heavy { width: 3px; }
#stam-label { color: var(--dim); font-size: 10px; min-width: 14px; }

#controls { display: flex; gap: 8px; }
.cmd { flex: 1; background: #182032; border: 1px solid var(--line);
  border-radius: 8px; padding: 13px; font-weight: 600; letter-spacing: 0.06em; }
.cmd:active { background: #22304a; }
.cmd.on { border-color: var(--blue); color: var(--blue); }
/* SHARE lights up only where it is legal: inside the base radius */
.cmd.share { color: var(--dim); }
.cmd.share.on { border-color: var(--green); color: var(--green); background: #12251c; }

/* Ally-attack confirmation. Armed only by a long press ON a teammate, and only
   this bar can send it — it sits below the map so a stray tap in a crowd has no
   way of reaching it. Loud on purpose: a betrayal should never look routine. */
#betray { display: flex; align-items: center; gap: 8px;
  border: 1px solid #6a2a2a; border-radius: 8px; background: #1c1114; padding: 6px 8px; }
#betray[hidden] { display: none; }
#betray-who { flex: 1; font-size: 12px; color: var(--red);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmd.danger { flex: 0 0 auto; padding: 10px 14px; border-color: #8a3333;
  color: var(--red); background: #2a1418; }
.cmd.danger:active { background: #3a1a20; }

/* tiny settings row under the controls */
#settings { display: flex; align-items: center; gap: 8px; font-size: 11px; }
/* The readout is the one thing on this line that may LOSE space: it is nowrap (digits
   must not dance), so without min-width:0 it refuses to shrink and a third toggle
   (SOUND) is enough to push a 390 px phone into horizontal overflow. */
#settings .nethud { flex: 1; min-width: 0; text-align: right; overflow: hidden;
  text-overflow: ellipsis; }

/* what the map does, on its own line so it is never ellipsised away */
#tips { font-size: 11px; line-height: 1.3; color: #6c7688; text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#tips b { color: var(--text); font-weight: 600; }
.toggle { background: #141a28; border: 1px solid var(--line); border-radius: 6px;
  padding: 5px 9px; font-size: 10px; letter-spacing: 0.06em; color: var(--dim); }
.toggle.on { border-color: #2a6a3a; color: var(--green); }

/* Always-available connection readout: ping / fps / pos-frame rate / lost frames.
   Monospace so the digits do not dance, colour-coded so a bad link is legible on a
   phone without opening a console. */
.nethud { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 10px; color: #6d7789; white-space: nowrap; letter-spacing: -0.01em; }
.nethud.warn { color: var(--gold); }
.nethud.bad { color: var(--red); }

/* ---- FRAME BUDGET readout ---------------------------------------------------
   The chip lives in the topbar and is ALWAYS visible; tapping it opens the panel
   over the map. Its colour is HEADROOM (idle ms / frame ms), not fps: this runs on
   a 60 Hz phone and a 120 Hz iPad, so "58" means two different things and "9% of
   the frame left" means the same thing on both. */
.perfchip { display: flex; flex-direction: column; align-items: center; gap: 0;
  margin-left: 6px; padding: 2px 5px; min-width: 34px; line-height: 1;
  background: rgba(255,255,255,.03); border: 1px solid #232a36; border-radius: 6px;
  color: inherit; font: inherit; -webkit-tap-highlight-color: transparent; }
.perfchip b { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px; font-weight: 700; color: var(--green); }
.perfchip i { font-style: normal; font-size: 7px; letter-spacing: .12em; color: #5c6577; }
.perfchip.warn b { color: var(--gold); }
.perfchip.bad b { color: var(--red); }
.perfchip.bad { border-color: #6a2a2a; }
.perfchip.on { background: rgba(255,255,255,.08); }

#perf-panel { position: absolute; top: 4px; right: 4px; width: 208px; z-index: 6;
  pointer-events: none; padding: 6px 7px 5px; border-radius: 7px;
  background: rgba(9,11,16,.92); border: 1px solid #232a36;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 10px; line-height: 1.45; color: #98a1b3; font-variant-numeric: tabular-nums; }
#perf-panel .spk { display: block; width: 100%; height: 26px; }
#perf-panel .spk path { fill: none; stroke-linecap: butt; }
#perf-panel .spk .gd { stroke: rgba(255,255,255,.11); stroke-width: 1; }
#perf-panel .spk .s0 { stroke: #4fbf86; }
#perf-panel .spk .s1 { stroke: #e3b74a; }
#perf-panel .spk .s2 { stroke: #e05c3c; }
#perf-panel .pax { display: flex; justify-content: space-between; font-size: 8px;
  color: #545d70; margin: 1px 0 4px; }
#perf-panel .pr { display: flex; align-items: baseline; gap: 5px; }
#perf-panel .pr s { flex: 0 0 46px; text-decoration: none; color: #6d7789; }
#perf-panel .pr em { flex: 0 0 56px; font-style: normal; color: #dfe5f0; }
/* the note column never wraps: one two-line note pushes every row below it down */
#perf-panel .pr u { text-decoration: none; color: #545d70; font-size: 9px;
  white-space: nowrap; overflow: hidden; min-width: 0; }
#perf-panel .pr.warn em { color: var(--gold); }
#perf-panel .pn { margin-top: 5px; padding-top: 4px; border-top: 1px solid #ffffff12;
  font-size: 8.5px; line-height: 1.35; color: #5d667a; white-space: normal; }

#dead-overlay .lost { color: #b98f3e; font-size: 12px; }

#feed { list-style: none; flex: 1 1 auto; min-height: 40px; overflow-y: auto;
  font-size: 12px; background: var(--panel); border: 1px solid var(--line);
  border-radius: 8px; padding: 6px 8px; }
#feed li { color: var(--dim); padding: 1px 0; }
#feed li.kill { color: var(--text); }
#feed li.cap { color: var(--gold); }
#feed li.war { color: var(--blue); font-weight: 600; }
#feed li.sys { color: #8a7a4a; }
/* v2.6 feed kinds. `kind` is an open vocabulary — anything unknown still lands
   in the default dim style, so a future kind is never invisible. */
#feed li.relic { color: var(--gold); }
#feed li.zone { color: var(--amber); font-weight: 600; }

#chat-form { display: flex; gap: 6px; }
#chat-form input { padding: 9px 10px; font-size: 14px; }
#chat-form button { padding: 9px 12px; font-size: 12px; }

body.locked #controls .cmd, body.locked #chat-form button { opacity: .45; pointer-events: none; }
body.locked #mapwrap { filter: grayscale(.7) brightness(.8); }

/* ------------------------------ deploy watch ------------------------------
   The server is serving a newer build than this page loaded, so this client is
   about to reload itself. Fixed over the topbar rather than placed in the
   #game-view column: that column's map sizer budgets its rows in hard pixels
   (see resize()), so an extra row there would push the control pad off a short
   screen. Top of the window also means the map, the HUD and the pad all stay
   visible and usable for the whole grace period. */
#update-note {
  position: fixed; top: 0; left: 0; right: 0; z-index: 90;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; padding-top: calc(8px + env(safe-area-inset-top));
  background: #1d1607f7; border-bottom: 1px solid var(--gold);
  font-size: 12px; line-height: 1.35; color: #ffe9ad;
  box-shadow: 0 4px 18px #000a;
}
#update-note[hidden] { display: none; }
#update-note .u-txt { flex: 1; min-width: 0; }
#update-note b { color: var(--gold); }
#update-now {
  background: linear-gradient(#ffdc82, #e0a92c); border: 0; border-radius: 6px;
  padding: 9px 11px; font-size: 11px; font-weight: 800; letter-spacing: 0.06em;
  color: #12100a; white-space: nowrap;
}
