/* ============================================================================
   Drag(*)nBall Z: Fighter's Edition — WEB CLIENT chrome (css/client.css)
   ----------------------------------------------------------------------------
   Styles for the /play terminal's client features (js/views/play.js): themes,
   font/size vars, the settings drawer, scouter HUD, macro row, scroll pill,
   command palette, highlight rules. Loaded AFTER app.css so overrides win by
   order — app.css itself is never touched.

   DEFAULT POSTURE: with a virgin settings blob no --term-* var and no
   [data-theme] is set, so every rule below falls back to app.css's exact
   values — the terminal is pixel-identical to before this file existed.
   ========================================================================= */

/* [hidden] must beat the display:flex on these rows/overlays */
.play-hud[hidden], .play-macros[hidden], .play-drawer[hidden],
.play-palette[hidden], .play-pill[hidden] { display: none !important; }

/* ---- the terminal instrument: extra grid rows + themable vars ------------- */
.play {
  grid-template-rows: auto minmax(0, 1fr) auto auto auto; /* bar / out / hud / macros / in — minmax(0,1fr)
     so the OUTPUT row has a 0 floor (Zhayl): a growing stream scrolls inside .play-out, it doesn't
     expand the row + push the input/layout down. Cockpit mode already uses minmax; classic did not. */
  background: var(--term-bg, var(--bg-well));
}
.play-out {
  font-family: var(--term-font, var(--mono));
  /* bigger low-vision-friendly default (Cockpit v2 #388): the unset fallback now
     lands ~18px, matching defaults().display.size; the size slider still wins via
     --term-size when the player customizes it. */
  font-size: var(--term-size, clamp(16px, 1.3vw, 19px));
  line-height: var(--term-lh, 1.25);
  color: var(--term-fg, #f4f7fb);
  background:
    radial-gradient(circle at 50% 120%, color-mix(in srgb, var(--term-accent, var(--green)) 5%, transparent), transparent 44%),
    linear-gradient(180deg, rgba(0,0,0,.06), transparent 18%);
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--term-accent, var(--green)) 34%, transparent) rgba(0,0,0,.15);
}
.play-out::-webkit-scrollbar { width: 9px; }
.play-out::-webkit-scrollbar-track { background: rgba(0,0,0,.15); }
.play-out::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--term-accent, var(--green)) 30%, transparent); border: 2px solid transparent; border-radius: 8px; background-clip: padding-box; }
.play-out::-webkit-scrollbar-thumb:hover { background-color: color-mix(in srgb, var(--term-accent, var(--green)) 52%, transparent); }
.play-in .play-command-input,
.play-in .play-command-password {
  font-family: var(--term-font, var(--mono));
  color: var(--term-fg, var(--fg));
  caret-color: var(--term-accent, var(--green));
}
.play-in .ps { color: var(--term-accent, var(--green)); }
.play-bar.connected .lamp {
  background: var(--term-accent, var(--green));
  box-shadow: 0 0 0.5em var(--term-accent, var(--green));
}
/* header-bar buttons — proper pill buttons (nicer than the old [bracket] text). Reserve the top-right
   corner so the floating PING chip (position:absolute, top-right) is never hidden under [disconnect]. */
.play-bar { padding-right: 6.4rem; }
.play-bar .x {
  user-select: none; cursor: pointer; color: var(--fg-dim);
  padding: 0.18rem 0.6rem; border: 1px solid var(--line-hot); border-radius: 4px;
  background: var(--bg-well); letter-spacing: 0.04em; line-height: 1.5; white-space: nowrap;
  transition: color 120ms, background 120ms, border-color 120ms, box-shadow 120ms;
}
.play-bar .x:hover {
  color: #03170d; background: var(--green); border-color: var(--green);
  box-shadow: 0 0 0.5em rgba(77,242,162,0.35);
}
.play-bar .x.disc:hover {
  color: #1a0303; background: var(--red); border-color: var(--red);
  box-shadow: 0 0 0.5em rgba(255,90,90,0.4);
}

/* ---- terminal color themes -------------------------------------------------
   A theme re-tints ONLY the chrome + default fg/bg; the in-game ANSI colors
   arrive as inline hex from term.js and stay authoritative. No data-theme
   attribute (the default "scouter") = zero overridden vars = today's look. */
.play[data-theme='amber']    { --term-bg: #050302; --term-fg: #ffdcae; --term-accent: #ffb454; --term-dim: #8a6d4a; }
.play[data-theme='classic']  { --term-bg: #000000; --term-fg: #e8e8e8; --term-accent: #9a9a9a; --term-dim: #6f6f6f; }
.play[data-theme='namek']    { --term-bg: #021208; --term-fg: #d8ffe9; --term-accent: #46f5b8; --term-dim: #3f8a68; }
.play[data-theme='contrast'] { --term-bg: #000000; --term-fg: #ffffff; --term-accent: #ffffff; --term-dim: #bbbbbb; }

/* ---- line-per-div scrollback ---------------------------------------------- */
/* an empty finalized line must keep its height (pre-wrap divs collapse) */
.play-out .tline:empty::before { content: '\200B'; }
.play-out .tline {
  position: relative; min-height: 1.5em; margin-inline: -.45rem; padding-inline: .55rem;
  border-left: 2px solid transparent; border-radius: 2px;
  transition: background-color 100ms, border-color 100ms;
}
.play-out .tline:hover { background: rgba(255,255,255,.022); }
/* A hover timestamp stays out of the text flow, so inspecting a line never
   causes adjacent terminal text to jump. The explicit timestamp setting still
   uses the traditional inline stamp below. */
.play:not(.show-ts) .play-out .tline[data-ts]:hover::after {
  content: attr(data-ts); position: absolute; right: .5rem; top: .18em;
  padding: .05rem .3rem; border-radius: 3px; pointer-events: none;
  color: var(--term-dim, var(--fg-faint)); background: rgba(2,6,4,.88);
  font-size: .68em; line-height: 1.35; letter-spacing: .04em;
}
.play-out .tline.feed-start { margin-top: .28rem; }
.play-out .tline.evt-chat { border-left-color: #65bfff; background-color: rgba(70,164,235,.035); }
.play-out .tline.evt-combat { border-left-color: #ff665f; background-color: rgba(255,77,77,.035); }
.play-out .tline.evt-reward { border-left-color: #ffc24b; background-color: rgba(255,194,75,.04); }
.play-out .tline.evt-travel { border-left-color: #69dfa4; background-color: rgba(77,242,162,.025); }
.play-out .tline.evt-system { border-left-color: #a792ff; background-color: rgba(152,126,255,.035); }
.play-out .tline.evt-chat:hover { background-color: rgba(70,164,235,.075); }
.play-out .tline.evt-combat:hover { background-color: rgba(255,77,77,.075); }
.play-out .tline.evt-reward:hover { background-color: rgba(255,194,75,.08); }
.play-out .tline.evt-travel:hover { background-color: rgba(77,242,162,.06); }
.play-out .tline.evt-system:hover { background-color: rgba(152,126,255,.075); }

/* Combat impact language: only the isolated backdrop animates. Text never moves, scales, or blurs. */
.play-out .tline.combat-impact { overflow: hidden; isolation: isolate; --impact-color: #ff665f; }
.play-out .tline.combat-impact::after {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  left: -.2rem; top: 50%; width: 2.3rem; height: 2.3rem; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--impact-color) 65%, transparent);
  opacity: 0; transform: translate(-38%, -50%) scale(.25);
}
.play-out .tline.combat-impact.combat-fx-reduced { animation: combat-line-soft 360ms ease-out both; }
.play-out .tline.combat-impact.combat-fx-full { animation: combat-line-full 460ms ease-out both; }
.play-out .tline.combat-impact.combat-fx-full::after,
.play-out .tline.combat-impact.combat-heavy.combat-fx-reduced::after { animation: combat-ring 440ms ease-out both; }
.play-out .tline.combat-parry { --impact-color: #ffd45d; border-left-color: #ffd45d; }
.play-out .tline.combat-block { --impact-color: #72caff; border-left-color: #72caff; }
.play-out .tline.combat-barrier { --impact-color: #bb8cff; border-left-color: #bb8cff; }
.play-out .tline.combat-deflect { --impact-color: #62f4e4; border-left-color: #62f4e4; }
.play-out .tline.combat-evade { --impact-color: #d7e6f3; border-left-color: #8faabd; }
.play-out .tline.combat-windup { --impact-color: #ffb451; border-left-color: #ffb451; }
/* Audience meaning always wins over the technique vocabulary above it:
   blue = you acted, red = aimed at you, gold = witnessed exchange. */
.play-out .tline.combat-role-a { --impact-color: #57b9ff; border-left-color: #57b9ff; }
.play-out .tline.combat-role-t { --impact-color: #ff4f59; border-left-color: #ff4f59; }
.play-out .tline.combat-role-o { --impact-color: #f2bd45; border-left-color: #f2bd45; }
.play-out .tline.combat-heavy { border-left-width: 3px; }
.play-out.combat-kick { animation: combat-output-kick 180ms ease-out; }
@keyframes combat-line-soft {
  0% { background-color: color-mix(in srgb, var(--impact-color) 14%, transparent); }
  100% { background-color: rgba(255,77,77,.035); }
}
@keyframes combat-line-full {
  0% { background-color: color-mix(in srgb, var(--impact-color) 25%, transparent); box-shadow: inset 8px 0 16px color-mix(in srgb, var(--impact-color) 12%, transparent); }
  100% { background-color: rgba(255,77,77,.035); box-shadow: inset 0 0 0 transparent; }
}
@keyframes combat-ring {
  0% { opacity: .8; transform: translate(-38%, -50%) scale(.25); }
  100% { opacity: 0; transform: translate(-38%, -50%) scale(1.65); }
}
@keyframes combat-output-kick {
  0%, 100% { transform: translateX(0); }
  30% { transform: translateX(1px); }
  65% { transform: translateX(-1px); }
}
@media (prefers-reduced-motion: reduce) {
  .play-out .tline.combat-impact { animation-duration: 1ms !important; }
  .play-out .tline.combat-impact::after, .play-out.combat-kick { animation: none !important; }
}
/* optional [HH:MM:SS] stamps — every finalized line carries data-ts; the
   .show-ts class (Display > timestamps) reveals them, including on old lines */
.play.show-ts .tline[data-ts]::before {
  content: attr(data-ts) '  ';
  color: var(--term-dim, var(--fg-faint));
  font-size: 0.82em;
}
/* local echo: the EXPANDED outbound line, dim */
.play-out .tline.echo { color: var(--term-dim, var(--fg-dim)); }

/* ---- modern command deck ------------------------------------------------- */
.play-in {
  position: relative; align-items: center; gap: .65rem; min-height: 3.2rem;
  padding: .48rem .72rem; border-top-color: color-mix(in srgb, var(--term-accent, var(--green)) 28%, var(--line));
  background: linear-gradient(180deg, rgba(15,28,22,.97), rgba(5,11,8,.99));
  box-shadow: 0 -9px 25px rgba(0,0,0,.2), inset 0 1px rgba(162,255,211,.025);
}
.play-in-label {
  flex: none; color: var(--term-accent, var(--green)); font: 700 .62em/1 var(--mono);
  letter-spacing: .15em; text-shadow: 0 0 9px color-mix(in srgb, var(--term-accent, var(--green)) 42%, transparent);
}
.play-in .ps { flex: none; font-size: 1.45em; line-height: 1; filter: drop-shadow(0 0 5px color-mix(in srgb, var(--term-accent, var(--green)) 45%, transparent)); }
.play-in .play-command-input,
.play-in .play-command-password {
  flex: 1 1 auto; min-width: 0; box-sizing: border-box; height: 2.2rem; padding: .38rem .68rem;
  border: 1px solid color-mix(in srgb, var(--term-accent, var(--green)) 22%, var(--line)); border-radius: 7px;
  outline: none; background:
    linear-gradient(90deg, color-mix(in srgb, var(--term-accent, var(--green)) 4%, transparent), transparent 22%),
    rgba(0,0,0,.34);
  box-shadow: inset 0 2px 9px rgba(0,0,0,.32), inset 3px 0 0 color-mix(in srgb, var(--term-accent, var(--green)) 42%, transparent);
  transition: border-color 130ms, box-shadow 130ms, background 130ms;
}
.play-in .play-command-input[hidden],
.play-in .play-command-password[hidden] { display: none !important; }
.play-in .play-command-input {
  display: block; overflow-x: auto; overflow-y: hidden; white-space: pre; line-height: 1.38;
  scrollbar-width: none;
}
.play-in .play-command-input::-webkit-scrollbar { display: none; }
.play-in .play-command-input:empty::before {
  content: attr(data-placeholder); color: var(--term-dim, var(--fg-faint)); opacity: .72; pointer-events: none;
}
.play-in .play-command-input:hover,
.play-in .play-command-password:hover { border-color: color-mix(in srgb, var(--term-accent, var(--green)) 42%, var(--line)); }
.play-in .play-command-input:focus,
.play-in .play-command-password:focus {
  border-color: color-mix(in srgb, var(--term-accent, var(--green)) 78%, white 8%);
  background: linear-gradient(90deg, color-mix(in srgb, var(--term-accent, var(--green)) 7%, transparent), transparent 25%), rgba(1,10,6,.52);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--term-accent, var(--green)) 11%, transparent), 0 0 16px color-mix(in srgb, var(--term-accent, var(--green)) 7%, transparent), inset 3px 0 0 var(--term-accent, var(--green));
}
.play-in.input-disabled .play-command-input { opacity: .48; cursor: not-allowed; }
.play-in-hint { flex: none; color: var(--fg-faint); font: 600 .56em/1 var(--mono); letter-spacing: .08em; white-space: nowrap; opacity: .72; }
@media (max-width: 900px) {
  .play-in { gap: .42rem; min-height: 3rem; padding-inline: .5rem; }
  .play-in-label, .play-in-hint { display: none; }
  .play-in .play-command-input, .play-in .play-command-password { font-size: max(16px, .95em); }
  .play-out .tline { padding-right: .55rem; }
  .play:not(.show-ts) .play-out .tline[data-ts]:hover::after { display: none; }
}

/* highlight rules: 2px left rule + a translucent wash of the rule's color;
   the game's own colored spans inside the line are untouched */
.play-out .tline.hl {
  box-shadow: inset 2px 0 0 var(--hl-c, var(--gold));
  background: color-mix(in srgb, var(--hl-c, #ffc24b) 12%, transparent);
}
@keyframes dbzHlFlash {
  0%   { background: color-mix(in srgb, var(--hl-c, #ffc24b) 45%, transparent); }
  100% { background: color-mix(in srgb, var(--hl-c, #ffc24b) 12%, transparent); }
}
.play-out .tline.hl-flash { animation: dbzHlFlash 900ms ease-out 1; }

/* ---- "N new lines" pill (split-scrollback) --------------------------------- */
.play-pill {
  position: absolute; right: 1.1rem; z-index: 6;
  font-family: var(--mono); font-size: 0.78em; letter-spacing: 0.06em;
  padding: 0.2rem 0.7rem; cursor: pointer; user-select: none;
  color: #03170d; background: var(--term-accent, var(--green));
  border: 1px solid color-mix(in srgb, var(--term-accent, var(--green)) 65%, #fff 12%);
  border-radius: 999px; box-shadow: 0 7px 22px rgba(0,0,0,.55), 0 0 16px color-mix(in srgb, var(--term-accent, var(--green)) 16%, transparent);
  animation: play-pill-in 150ms ease-out both;
}
@keyframes play-pill-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* ---- scouter HUD row (Extras > HUD) ---------------------------------------- */
.play-hud {
  display: flex; align-items: center; gap: 0.7ch; flex-wrap: wrap;
  padding: 0.25rem 0.9rem; border-top: 1px solid var(--line);
  font-family: var(--term-font, var(--mono)); font-size: 0.8em; letter-spacing: 0.04em;
  color: var(--term-dim, var(--fg-dim));
}
.play-hud .hud-k { color: var(--term-dim, var(--fg-faint)); letter-spacing: 0.18em; }
.play-hud .hud-pl { margin-left: 1.4ch; }
.play-hud .hud-meter { display: inline-flex; gap: 2px; }
.play-hud .hud-meter i { width: 0.7ch; height: 0.62em; background: var(--line-hot); }
.play-hud .hud-meter i.on.g { background: var(--green); }
.play-hud .hud-meter i.on.y { background: var(--amber); }
.play-hud .hud-meter i.on.r { background: var(--red); }
.play-hud .hud-meter i.on.c { background: #6fe9f0; }
.play-hud .hud-v { color: var(--term-fg, var(--fg)); }
.play-hud .hud-v.g { color: var(--green); }
.play-hud .hud-v.y { color: var(--amber); }
.play-hud .hud-v.r { color: var(--red); }
.play-hud .hud-v.c { color: #6fe9f0; }
.play-hud .plm { height: 0.55em; margin-right: 0.2ch; }

/* ---- macro-button row (Keys > macros) --------------------------------------- */
.play-macros {
  display: flex; flex-wrap: wrap; gap: 0.35rem;
  padding: 0.3rem 0.7rem; border-top: 1px solid var(--line);
}
.play-macros button {
  font-family: var(--term-font, var(--mono)); font-size: 0.78em; letter-spacing: 0.06em;
  padding: 0.18rem 0.6rem; cursor: pointer;
  color: var(--term-accent, var(--green)); background: transparent;
  border: 1px solid var(--line-hot); border-radius: 0;
}
.play-macros button:hover {
  background: var(--bg-raise);
  border-color: var(--term-accent, var(--green));
}

/* ---- settings drawer --------------------------------------------------------
   Slides in from the right, between the status bar and the input row (JS sets
   top/bottom to the live bar/input heights). Scouter-native: corner ticks like
   .modal-win, accent trim from the active theme. */
.play-drawer {
  position: absolute; right: 0; z-index: 10;
  width: min(340px, 88%);
  display: flex; flex-direction: column;
  /* Options is a popup surface too: inherit the independent UI-size setting instead of the page's
     fixed base size. Child em values preserve hierarchy while the whole drawer scales consistently. */
  font-size: var(--uih-size, 16px);
  background: var(--bg-win);
  border-left: 1px solid var(--line-lock);
  transform: translateX(105%);
  transition: transform 180ms ease-out;
  box-shadow: -0.6em 0 1.6em rgba(0, 0, 0, 0.55);
}
.play-drawer.open { transform: translateX(0); }
.play-drawer::before {
  content: ''; position: absolute; inset: -1px;
  pointer-events: none; z-index: 3;
  --tk: var(--term-accent, var(--green));
  background-image:
    linear-gradient(var(--tk), var(--tk)), linear-gradient(var(--tk), var(--tk)),
    linear-gradient(var(--tk), var(--tk)), linear-gradient(var(--tk), var(--tk));
  background-repeat: no-repeat;
  background-position: left top, left top, left bottom, left bottom;
  background-size: 8px 1px, 1px 8px, 8px 1px, 1px 8px;
}
.pd-tabs {
  display: flex; gap: 0.3rem; flex-wrap: wrap;
  padding: 0.5rem 0.6rem 0.4rem;
  border-bottom: 1px solid var(--line);
}
.pd-tab {
  font-family: var(--mono); font-size: 0.72em; letter-spacing: 0.08em;
  padding: 0.22rem 0.55rem; cursor: pointer;
  color: var(--green); background: transparent;
  border: 1px solid var(--line-lock); border-radius: 0;
}
.pd-tab:hover { color: #03170d; background: var(--green); border-color: var(--green); }
.pd-tab.on { color: #1a0e00; background: var(--orange); border-color: var(--orange); }
.pd-body { flex: 1; overflow-y: auto; padding: 0.6rem 0.7rem; font-size: 0.88em; }
.pd-foot {
  display: flex; align-items: center; gap: 0.6ch;
  padding: 0.4rem 0.7rem; border-top: 1px solid var(--line);
  font-size: 0.75em; color: var(--fg-faint);
}
.pd-foot .grow { flex: 1; }
.pd-foot .sled { width: 0.5rem; height: 0.5rem; background: var(--fg-faint); display: inline-block; border-radius: 1px; }
.pd-foot .sled.on { background: var(--green); box-shadow: 0 0 0.4em var(--green); }
.pd-foot .act { color: var(--fg-dim); cursor: pointer; white-space: nowrap; }
.pd-foot .act:hover { color: var(--orange); }

/* drawer form primitives */
.pd-h {
  color: var(--amber); font-family: var(--ui); font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; font-size: 0.76em;
  margin: 0.8rem 0 0.3rem;
}
.pd-row { display: flex; align-items: center; gap: 0.8ch; margin: 0.45rem 0; }
.pd-lbl {
  color: var(--fg-faint); text-transform: uppercase;
  letter-spacing: 0.12em; font-size: 0.72em; min-width: 7ch; flex-shrink: 0;
}
.pd-in, .pd-select {
  background: var(--bg-well); color: var(--fg);
  border: 1px solid var(--line-hot); border-radius: 0;
  font-family: var(--mono); font-size: 0.9em; padding: 0.25rem 0.45rem;
  min-width: 0;
}
.pd-in { flex: 1; }
.pd-in-name { flex: 0 1 9ch; }
.pd-in:focus, .pd-select:focus { outline: none; border-color: var(--line-lock); }
.pd-check {
  display: flex; align-items: center; gap: 0.5ch;
  margin: 0.4rem 0; cursor: pointer; color: var(--fg-dim); font-size: 0.9em;
}
.pd-check input { accent-color: var(--green); }
.pd-check .pd-sub { color: var(--fg-faint); font-size: 0.82em; margin-left: 0.6ch; }
.pd-slider { display: inline-flex; align-items: center; gap: 0.8ch; flex: 1; }
.pd-slider input[type='range'] { flex: 1; accent-color: var(--green); min-width: 0; }
.pd-val { color: var(--amber); font-size: 0.85em; min-width: 4.5ch; text-align: right; }
.pd-btn {
  font-family: var(--mono); font-size: 0.78em; letter-spacing: 0.05em;
  padding: 0.22rem 0.6rem; cursor: pointer;
  color: var(--green); background: transparent;
  border: 1px solid var(--line-lock); border-radius: 0; white-space: nowrap;
}
.pd-btn:hover { color: #03170d; background: var(--green); border-color: var(--green); }
.pd-help { color: var(--fg-dim); font-size: 0.82em; margin: 0.2rem 0 0.5rem; line-height: 1.45; }
.pd-help b { color: var(--amber); font-weight: 400; }
.pd-err { color: var(--red); font-size: 0.82em; min-height: 1.1em; margin: 0.15rem 0; }
.pd-prev {
  color: var(--amber); font-size: 0.85em; min-height: 1.2em; margin-top: 0.25rem;
  white-space: pre-wrap; word-break: break-word;
}
.pd-note { color: var(--fg-faint); font-size: 0.78em; margin: 0.3rem 0; }

/* live display sample — rendered through the REAL ansi renderer + term vars */
.pd-sample {
  border: 1px solid var(--line); background: var(--term-bg, var(--bg-well));
  padding: 0.4rem 0.6rem; margin-bottom: 0.55rem;
  font-family: var(--term-font, var(--mono));
  font-size: var(--term-size, 1em);
  line-height: var(--term-lh, 1.25);
  color: var(--term-fg, #f4f7fb);
  white-space: pre-wrap; word-break: break-word;
}

/* theme swatch grid */
.pd-themes { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.pd-theme {
  display: inline-flex; align-items: center; gap: 0.5ch;
  background: transparent; border: 1px solid var(--line-hot); border-radius: 0;
  padding: 0.2rem 0.45rem; cursor: pointer;
  color: var(--fg-dim); font-family: var(--mono); font-size: 0.76em;
}
.pd-theme:hover { border-color: var(--line-lock); color: var(--fg); }
.pd-theme.on { border-color: var(--orange); color: var(--fg); }
.pd-sw {
  width: 1.7rem; height: 0.95rem;
  display: inline-flex; align-items: center; justify-content: center; gap: 2px;
  border: 1px solid var(--line);
}
.pd-sw i { width: 4px; height: 60%; display: inline-block; }

/* list rows (aliases / macros / highlight rules) */
.pd-list { margin: 0.4rem 0; border: 1px solid var(--line); }
.pd-item {
  display: flex; align-items: center; gap: 0.6ch;
  padding: 0.26rem 0.5rem; border-bottom: 1px solid var(--line); font-size: 0.9em;
}
.pd-item:last-child { border-bottom: none; }
.pd-item .k { color: var(--green); white-space: nowrap; }
.pd-item .arrow { color: var(--fg-faint); }
.pd-item .b { color: var(--fg-dim); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.pd-item .act { color: var(--fg-faint); cursor: pointer; white-space: nowrap; user-select: none; }
.pd-item .act:hover { color: var(--orange); }
.pd-item input[type='checkbox'] { accent-color: var(--green); }
.pd-item.off .k, .pd-item.off .b { opacity: 0.45; }
.pd-item .chip { width: 0.85em; height: 0.85em; display: inline-block; border: 1px solid var(--line); flex-shrink: 0; }
.pd-empty { padding: 0.35rem 0.5rem; color: var(--fg-faint); font-style: italic; font-size: 0.88em; }

/* numpad diagram (Keys tab) */
.pd-pad { display: grid; grid-template-columns: repeat(3, 3.1rem); gap: 2px; margin: 0.4rem 0; }
.pd-pad span {
  border: 1px solid var(--line-hot); text-align: center;
  padding: 0.22rem 0; font-size: 0.76em; color: var(--fg-faint);
}
.pd-pad span b { color: var(--green); font-weight: 400; margin-left: 0.4ch; }

/* ---- command palette (Ctrl+K) ----------------------------------------------- */
.play-palette {
  position: absolute; inset: 0; z-index: 20;
  background: rgba(2, 4, 3, 0.62);
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 7%;
}
.pal-card {
  width: min(30rem, 92%);
  background: var(--bg-win); border: 1px solid var(--line-lock);
  box-shadow: 0 0.8em 2em rgba(0, 0, 0, 0.6);
}
.pal-in {
  width: 100%; box-sizing: border-box;
  background: var(--bg-well); color: var(--fg);
  border: none; border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-size: 0.95em;
  padding: 0.45rem 0.7rem; outline: none; caret-color: var(--green);
}
.pal-list { max-height: 16rem; overflow-y: auto; }
.pal-item {
  display: flex; align-items: baseline; gap: 1ch;
  padding: 0.28rem 0.7rem; cursor: pointer; font-size: 0.9em;
}
.pal-item.sel { background: var(--bg-raise); box-shadow: inset 2px 0 0 var(--green); }
.pal-item .pal-name { color: var(--green); min-width: 10ch; }
.pal-item .pal-usage {
  color: var(--fg-dim); flex: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.pal-item .pal-cat { color: var(--fg-faint); font-size: 0.76em; white-space: nowrap; }
.pal-hint { padding: 0.3rem 0.7rem; border-top: 1px solid var(--line); color: var(--fg-faint); font-size: 0.74em; }
.pal-none { padding: 0.4rem 0.7rem; color: var(--fg-faint); font-style: italic; font-size: 0.88em; }

/* ---- reduced motion ---------------------------------------------------------- */
/* (owner decision) the OS "reduce motion" setting is intentionally IGNORED — effects always play; the
   in-client master + per-effect toggles are the only way to reduce motion. (The reduced-motion kill
   rules that lived here were removed.) */

/* ============================================================================
   COCKPIT LAYOUT (client Phase 2) — the multi-pane HUD around the terminal.
   EVERYTHING below is scoped under .play.cockpit / .play.fx (or element
   classes that only exist in cockpit): CLASSIC mode (no .cockpit class) is
   pixel-identical Phase-1 — the rail element renders display:none and none
   of these rules apply.
   ========================================================================= */

/* master switch chrome — the always-visible [cockpit]/[classic] bar control */
.play-bar .x.mode.on {
  color: var(--term-accent, var(--green)); border-color: rgba(77,242,162,0.55);
  background: rgba(77,242,162,0.12);
}
.play-bar .x.mode.on:hover { color: #03170d; background: var(--green); }

/* ---- the cockpit grid -------------------------------------------------------- */
.play-rail { display: none; }
.play.cockpit {
  display: grid;
  /* Owner (2026-07-03): RAIL ON THE LEFT. The map/scouter/nearby/chat sit in the primary (left) field
     of view, while the text stream + input stay grouped as one contiguous read/type zone on the right —
     no more diagonal split (text far-left, panels far-right) across a wide screen. */
  /* Wider rail (Cockpit v2 follow-up): the ASCII map is width-bound in this column, so a narrow rail
     forced tiny glyphs. A roomier rail lets fitMapFont scale the map up to a readable size. */
  /* (movable UI) the rail column width is a CSS var so the rail<->terminal splitter can set it live
     (persisted per character); absent -> the responsive clamp default. */
  grid-template-columns: var(--rail-w, clamp(340px, 32vw, 520px)) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto auto auto;
  grid-template-areas: "bar bar" "rail out" "rail hud" "rail macros" "rail in";
}
.play.cockpit > .play-bar { grid-area: bar; }
/* (Zhayl) DISPLAY > "CRT / scouter tint": hide the global scanline+glow+vignette overlay. */
body.no-crt .crt { display: none; }
/* (Zhayl) DISPLAY > "slim the terminal bar": collapse the top bar to a thin strip so the output reclaims
   the height; hovering it (or focusing a control inside) expands it back for the mode switch + settings. */
.play.cockpit.bar-slim > .play-bar {
  max-height: 6px; padding-top: 0; padding-bottom: 0; overflow: hidden; opacity: 0.22;
  transition: max-height 160ms ease, opacity 160ms ease, padding 160ms ease;
}
.play.cockpit.bar-slim > .play-bar:hover,
.play.cockpit.bar-slim > .play-bar:focus-within {
  max-height: 3rem; opacity: 1; padding-top: 0.3rem; padding-bottom: 0.3rem;
}
.play.cockpit > .play-out { grid-area: out; min-width: 0; }
.play.cockpit > .play-hud { display: none !important; } /* re-homed as the STATS pane */
.play.cockpit > .play-macros { grid-area: macros; }
.play.cockpit > .play-in { grid-area: in; }
.play.cockpit > .play-rail {
  grid-area: rail;
  display: flex; flex-direction: column; gap: 6px;
  /* (movable UI) split-pane weights make the panels sum to fit exactly, so the rail never scrolls -
     overflow is HIDDEN (a scrollbar was the "everything should just fit" complaint). Mobile re-enables
     scroll for the single full-area sheet via the max-width:900px rule below. */
  padding: 6px; min-height: 0; overflow-y: hidden; position: relative;
  border-right: 1px solid var(--line); /* rail now on the LEFT — divider on its right edge */
  background: var(--bg-pane, var(--bg-win));
  /* Zhayl: the rail (MAP + stats/nearby chips/gauges) is now a FIXED size, DECOUPLED from the DISPLAY > size
     slider. The slider used to anchor the rail's base font, so a bigger slider grew the em-sized rail
     furniture and STOLE height from the map card — shrinking the map (players saw "smaller font = bigger
     map"). The map is a stable fit-to-pane size; the slider scales only the output stream + chat (below). */
  font-size: clamp(15px, 1.15vw, 17px);
}

/* ---- pane cards (the drawer's corner-tick language) --------------------------- */
.pr-card {
  position: relative; display: flex; flex-direction: column; min-height: 0;
  border: 1px solid var(--line-hot); background: var(--bg-win); flex: none;
}
.pr-map { container-type: inline-size; }
.pr-card::before {
  content: ''; position: absolute; inset: -1px; pointer-events: none; z-index: 3;
  --tk: var(--term-accent, var(--green));
  background-image:
    linear-gradient(var(--tk), var(--tk)), linear-gradient(var(--tk), var(--tk)),
    linear-gradient(var(--tk), var(--tk)), linear-gradient(var(--tk), var(--tk));
  background-repeat: no-repeat;
  background-position: left top, left top, right bottom, right bottom;
  background-size: 8px 1px, 1px 8px, 8px 1px, 1px 8px;
}
.pr-card.off { display: none !important; }
.pr-hd {
  display: flex; align-items: baseline; gap: 0.6ch; flex: none;
  padding: 0.24rem 0.55rem; border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-size: 0.7em; letter-spacing: 0.14em;
  color: var(--fg-faint); text-transform: uppercase; user-select: none;
}
.pr-hd .t { color: var(--amber); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pr-hd .sub { color: var(--fg-faint); letter-spacing: 0.05em; text-transform: none; white-space: nowrap; }
.pr-hd .grow { flex: 1; }
/* Fixed-height environmental telemetry. Location/coordinates live below the
   render; this upper strip never wraps or changes the map's height. */
.pr-status {
  display: block; min-width: 0; overflow: hidden; padding: .3rem .55rem;
  background: rgba(0,0,0,.14); font-size: .78em; line-height: 1.15;
}

/* standby (pre-game) / offline (link lost) pane states */
.pr-card.standby > :not(.pr-hd) { opacity: 0.22; }
.pr-card.standby::after {
  content: 'AWAITING SIGNAL'; position: absolute; left: 0; right: 0; top: 42%;
  text-align: center; letter-spacing: 0.3em; font-size: 0.6em;
  color: var(--fg-faint); font-family: var(--mono); pointer-events: none;
}
.play-rail.offline .pr-card { opacity: 0.55; filter: saturate(0.4); }

/* ---- MAP pane ------------------------------------------------------------------ */
.pr-map .pr-env {
  display: grid; width: 100%; min-width: 0; gap: .24rem 0;
  grid-template-columns: repeat(4, minmax(0, auto));
  grid-template-areas: 'planet grav moon time' 'weather temp season season';
  overflow: hidden; white-space: nowrap;
}
.env-read {
  position: relative; display: flex; align-items: baseline; gap: .4ch;
  min-width: 0; overflow: hidden; padding: 0 .58rem; font-family: var(--mono); white-space: nowrap; user-select: none;
}
.env-read:first-child { padding-left: 0; }
.env-read + .env-read::before {
  content: ''; position: absolute; left: 0; top: 15%; bottom: 15%; width: 1px; background: rgba(129,205,166,.2);
}
.env-read i {
  color: var(--fg-faint); font-style: normal; font-size: .68em; font-weight: 600;
  letter-spacing: .13em; line-height: 1;
}
.env-read b { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--fg-dim); font-size: 1.02em; font-weight: 650; letter-spacing: .045em; line-height: 1; }
.env-read.env-planet { grid-area: planet; }
.env-read.env-grav { grid-area: grav; }
.env-read.env-moon { grid-area: moon; }
.env-read.env-time { grid-area: time; }
.env-read.env-weather { grid-area: weather; padding-left: 0; }
.env-read.env-temp { grid-area: temp; }
.env-read.env-season { grid-area: season; }
.env-read.env-planet b {
  color: var(--green); font-size: 1.14em; letter-spacing: .1em; text-transform: uppercase;
  text-shadow: 0 0 9px rgba(77,242,162,.28);
}
.env-read.env-weather b { color: #89d8ff; }
.env-read.env-time b { color: var(--amber); }
.env-read.env-temp b { color: #f0b37d; }
@container (max-width: 480px) {
  .pr-map .pr-env { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .env-read { padding-inline: .35rem; gap: .25ch; }
  .env-read i { font-size: .58em; letter-spacing: .07em; }
  .env-read.env-planet b { font-size: 1.05em; }
}
.pr-maploc {
  flex: none; min-height: 1.55rem; display: flex; align-items: center; gap: .6ch;
  min-width: 0; overflow: hidden; padding: .2rem .55rem;
  border-top: 1px solid var(--line); background: rgba(0,0,0,.16);
  font-family: var(--mono); font-size: .68em; letter-spacing: .08em;
}
.pr-maploc .t {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--amber); text-transform: uppercase;
}
.pr-maploc .sub { flex: none; color: var(--fg-faint); white-space: nowrap; }
/* (Zhayl) the map wrap is the FILL BOX: it grows to take the card's remaining height (flex:1), and the map
   is transform:scaled to fill it edge-to-edge (fitMapFont). The row-number gutter is pinned to the right. */
.pr-gridwrap { position: relative; overflow: hidden; margin: 4px 6px; flex: 1 1 auto; min-height: 0; }
.pr-grid {
  /* natural (un-scaled) size; fitMapFont() transform:scale()s it to STRETCH-FILL the box (Zhayl: no borders,
     no whitespace). position:absolute keeps it out of flow so the scale never disturbs the box. */
  position: absolute; top: 0; left: 0; width: max-content;
  transform-origin: top left; will-change: transform;
  /* line-height 1.0 (Zhayl): no half-leading, so the natural map height is exactly rows x glyph -> the
     scale fills top-to-bottom with NO whitespace band, and each row's box matches the gutter's flat
     line-height so the [N] numbers line up 1:1 (1.12's leading offset them + left a top/bottom gap). */
  font-family: var(--term-font, var(--mono)); white-space: pre; line-height: 1.0;
  color: var(--term-fg, #f4f7fb);
  font-weight: 700; /* BOLD glyphs (owner) -- heavier strokes read far better at small map sizes */
}
/* (Zhayl) row-number gutter — the [N] labels lifted out of the map render, pinned to the right edge; each
   row's line-height is set by fitMapFont to the STRETCHED map row height so the numbers line up 1:1. */
.pr-rownums {
  position: absolute; top: 0; right: 0; bottom: 0; width: 2.6ch;
  text-align: center; font-size: 0.8em;
  font-family: var(--term-font, var(--mono)); white-space: pre;
  color: var(--fg-faint); font-weight: 700; user-select: none;
  border-left: 1px solid var(--line); background: var(--bg-pane, var(--bg-win));
}
.pr-rownums .rn { position: absolute; left: 0; right: 0; text-align: center; overflow: hidden; }
/* timestamps would wreck the grid alignment — never inside the map */
.pr-grid .tline::before { content: none !important; }
.pr-grid .tline:empty::before { content: none; }
.pr-fx { position: absolute; inset: 0; pointer-events: none; z-index: 2; overflow: hidden; }
/* (MAP FX T1.1) the CANVAS render back end — same layer as the glyph grid; while it's mounted
   (.cv-on) the DOM grid stands down entirely (the canvas draws glyphs + beams + particles).
   !important: the desktop media query re-asserts `display:block` on the grid at higher
   specificity (.play.cockpit .pr-map .pr-grid) — the canvas gate must always win it. */
.pr-canvas { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none; }
.pr-map.cv-on .pr-grid { display: none !important; }
/* Experimental immortal-only procedural isometric renderer. Its projection has no horizontal rows,
   so the legacy distance gutter is intentionally removed; coordinates/exits remain in their normal UI. */
.pr-map.iso-on .pr-rownums { display: none !important; }
.pr-map.iso-on .pr-gridwrap {
  background: #020408;
  box-shadow: inset 0 0 2.5rem rgba(0, 0, 0, 0.72);
}
.pr-iso-canvas { image-rendering: auto; }
.pr-empty { color: var(--fg-faint); font-style: italic; font-size: 0.75em; padding: 2px 4px; }
/* (Zhayl) IMPAIRED (unconscious / blind / asleep): overlay the map with a label, dim the map + nearby, and
   mute in-room say/yell in chat -- the cockpit reflects that you can't perceive the world. */
.pr-impair {
  position: absolute; inset: 0; z-index: 4; display: flex; align-items: center; justify-content: center;
  /* PITCH BLACK -- unconscious/blind/asleep means you perceive nothing; only the state label shows. */
  background: #000; color: var(--amber); pointer-events: none;
  font-family: var(--term-font, var(--mono)); font-weight: 700; font-size: 0.9em;
  letter-spacing: 0.32em; text-transform: uppercase; text-shadow: 0 0 8px rgba(0, 0, 0, 0.85);
}
.pr-impair[hidden] { display: none; }
/* the map goes fully DARK (the opaque .pr-impair veil sits over it with the label); nearby is hidden entirely --
   you can't see the world while unconscious / blind / asleep. */
.play[data-impair="ko"] .pr-grid,
.play[data-impair="blind"] .pr-grid,
.play[data-impair="sleep"] .pr-grid { opacity: 0; }
.play[data-impair="ko"] .pr-nearby .nb-wrap,
.play[data-impair="blind"] .pr-nearby .nb-wrap,
.play[data-impair="sleep"] .pr-nearby .nb-wrap { visibility: hidden; }
.play[data-impair="ko"] .pr-chatlog .tline[data-chan="say"],
.play[data-impair="blind"] .pr-chatlog .tline[data-chan="say"],
.play[data-impair="sleep"] .pr-chatlog .tline[data-chan="say"] { opacity: 0.28; font-style: italic; }
.pr-compass {
  display: grid; grid-template-columns: repeat(3, 2.3rem); gap: 2px;
  justify-content: center; padding: 2px 0 6px; flex: none;
}
.pr-compass button {
  font-family: var(--mono); font-size: 0.68em; letter-spacing: 0.05em;
  height: 1.35rem; cursor: pointer; border-radius: 0; padding: 0;
  color: var(--fg-faint); background: transparent; border: 1px solid var(--line);
}
.pr-compass button.lit { color: var(--term-accent, var(--green)); border-color: var(--line-hot); }
.pr-compass button.lit:hover { background: var(--bg-raise); border-color: var(--term-accent, var(--green)); }
.pr-compass button:disabled { opacity: 0.3; cursor: default; }

/* DESKTOP (Cockpit v2 #388): drop the 3x3 compass D-pad rose — movement is
   typed / numpad on desktop, and the STATE-FRAME map pane needs no on-map
   buttons — reclaiming that vertical space for a bigger, grow-to-fit map. The
   exit lit-state DATA is still computed (renderCompass) for the mobile rose +
   effects; the touch .play-dpad stays gated to coarse/mobile pointers below. */
@media (min-width: 901px) {
  .play.cockpit .pr-compass { display: none; }
  /* the map card flexes to fill the rail (biased 2:1 over chat) so the grid
     grows on tall screens; the grid absolutely fills its wrap so fitMapFont
     reads a stable height budget independent of the current glyph size. */
  .play.cockpit .pr-map { flex: 2 1 0; min-height: 12rem; } /* basis 0 (Zhayl): 2:1 split vs chat, content-independent */
  .play.cockpit .pr-map .pr-gridwrap { flex: 1 1 auto; min-height: 0; }
  /* (map stability + resizable) The NEARBY card takes a FLEX-BASIS-0 weighted share of the rail, so its
     height is CONTENT-INDEPENDENT (many nearby mobs never grow it / squeeze the MAP — the old jitter),
     yet the roster FILLS whatever height the card has and only scrolls beyond THAT. So dragging the pane
     taller now grows the roster to fill the whole box before it scrolls (owner report). The pane-resize
     drag overrides this default weight with an inline flex. */
  .play.cockpit .pr-nearby { flex: 0.7 1 0; min-height: 3.6rem; }
  .play.cockpit .pr-nearby .nb-wrap {
    flex: 1 1 0; min-height: 0; overflow-y: auto; overscroll-behavior: contain; align-content: flex-start;
  }
  /* (Zhayl) FILL BY STRETCH: the grid is natural-size (top-left) and transform:scale()d by fitMapFont to
     fill the ENTIRE box -- overriding the old absolute-inset+flex-center that left aspect-ratio margins
     (the whitespace) and, by pre-filling the box, defeated fitMapFont's measurement. */
  .play.cockpit .pr-map .pr-grid {
    position: absolute; top: 0; left: 0; right: auto; bottom: auto; inset: auto; width: max-content;
    display: block; transform-origin: top left;
  }
}

/* ---- STATS pane — a crafted scouter power gauge --------------------------------
   The PL/EN rows are real energy meters, not flat filled rectangles: a recessed,
   beveled channel (layered inset shadows) + fine segment ticks, holding a SHADED
   energy column (top gloss -> body -> deep base) with an outer glow. A restrained
   sheen sweeps the filled span. EN keeps its own energy hue (cyan-green); the PL
   row inherits the green health tone and is re-themed by the current form. */
/* the scouter title is the player identity ("THUNDERZ the Saiyan") — the JS emits the name upper-
   cased and the race title-case, so drop the header's uppercase transform to keep that mixed case. */
.pr-stats .pr-hd .t { text-transform: none; letter-spacing: 0.05em; }
.pr-stats .stat-rows { padding: 5px 8px 6px; display: flex; flex-direction: column; gap: 5px; }
.stat-row { display: flex; align-items: stretch; font-family: var(--mono); }
/* the PL gauge wraps the bar + the effect overlay, so effects can bleed just outside the track */
.pl-gauge { position: relative; flex: 1; min-width: 0; display: flex; }
.pl-gauge .stat-bar { flex: 1; }

/* (Tenkaichi-style bars) a SLANTED parallelogram track with a glossy, top-lit energy fill — the
   Budokai-Tenkaichi HP/ki look: angled ends, a bright vertical+horizontal gradient, a gloss sheen band
   across the top, and a hot leading edge. Label + value embedded inside. No segment slats. */
.stat-bar {
  flex: 1; height: 1.2em; position: relative; overflow: hidden;
  /* slanted ends -> the fighting-game parallelogram silhouette */
  clip-path: polygon(0.5em 0, 100% 0, calc(100% - 0.5em) 100%, 0 100%);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(0, 0, 0, 0.5)), var(--bg-well, #06090b);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--line-hot) 55%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, 0.10);
}
/* DELAYED-DAMAGE ghost trail — a pale column BEHIND the fill marking recently-lost ground (the
   fighting-game "white damage" read, hue-independent). Width driven by JS (setGauge). */
.stat-ghost {
  position: absolute; top: 0; bottom: 0; left: 0; width: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(205, 220, 226, 0.35));
  opacity: 0.5;
}
/* the glossy energy fill — vertical top-light gradient over a left-dark -> right-bright length gradient. */
.stat-fill {
  --fill: var(--en-base, var(--green));
  position: absolute; top: 0; bottom: 0; left: 0; width: 0; z-index: 2;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--fill) 72%, #ffffff) 0%, var(--fill) 44%, color-mix(in srgb, var(--fill) 78%, #000000) 100%),
    linear-gradient(90deg, color-mix(in srgb, var(--fill) 78%, #000000) 0%, var(--fill) 100%);
  box-shadow: 0 0 10px color-mix(in srgb, var(--fill) 60%, transparent);
}
/* glossy sheen band across the top third — the Tenkaichi shine. */
.stat-fill::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 44%; z-index: 4; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0));
}
/* the leading ENERGY FRONT — a crisp bright edge riding the fill's right side. */
.stat-fill::after {
  content: ''; position: absolute; top: 0; bottom: 0; right: 0; width: 2px; z-index: 5;
  background: #ffffff; box-shadow: 0 0 8px 1px color-mix(in srgb, var(--fill) 90%, #ffffff);
}
.play.fx .stat-fill { transition: width 260ms cubic-bezier(0.2, 0.8, 0.3, 1); }
.stat-fill.warn { --fill: var(--amber); }
.stat-fill.crit { --fill: var(--red); }
.stat-fill.over { --fill: #6fe9f0; }
/* ENERGY bar is PERCENT-colored (MUD prompt percent_color parity): >50% blue, 25-50% yellow, <=25% red,
   >100% cyan — scoped to the EN bar so the PL bar keeps its race/form tint. The tone classes (warn/crit/
   over) are applied by frameRenderStats at the matching thresholds. */
.stat-row .stat-bar:not(.pl-bar) { --en-base: #5b8cff; }                        /* high: blue */
.pr-stats .stat-row .stat-bar:not(.pl-bar) .stat-fill.warn { --fill: #ffc24b; } /* mid 25-50%: yellow */
.pr-stats .stat-row .stat-bar:not(.pl-bar) .stat-fill.crit { --fill: #ff5a5a; } /* low <=25%: red */
.pr-stats .stat-row .stat-bar:not(.pl-bar) .stat-fill.over { --fill: #6fe9f0; } /* over 100%: cyan */
.play.fx .stat-fill.crit { animation: dbzStatCrit 900ms ease-in-out infinite; }
@keyframes dbzStatCrit { 50% { opacity: 0.6; } }

/* embedded LABEL (PL / EN) — left, small, over a dark text-scrim so it reads on any fill. */
.stat-tag {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%); z-index: 6;
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.18em; font-weight: 700;
  color: rgba(255, 255, 255, 0.7); text-shadow: 0 0 3px #000, 0 1px 2px #000;
  pointer-events: none; user-select: none;
}
/* embedded VALUE — right, bright white, tabular; a hard text-shadow keeps it crisp over any fill. */
.stat-num {
  position: absolute; right: 11px; top: 50%; transform: translateY(-50%); z-index: 6;
  font-family: var(--mono); font-size: 0.74rem; font-weight: 700; letter-spacing: 0.02em;
  color: #ffffff; font-variant-numeric: tabular-nums; white-space: nowrap;
  text-shadow: 0 0 3px #000, 0 0 6px #000, 0 1px 2px #000; pointer-events: none;
}
.stat-num.crit { color: #ffd2d2; }
.pr-stats .plm { height: 0.55em; flex: none; }

/* ============================================================================
   FORM-EFFECT LADDER (#446 grown up) — the PL gauge reflects the current FORM.
   --form-color (from the form's fColor) themes the fill; a curated per-form
   profile (play.js formProfile) sets --fx-c1/--fx-c2/--fx-vein/--fx-pow + toggles
   the layer classes on .pr-stats, escalating with canonical strength:
     aura (SSJ) -> +electricity (SSJ2/3) -> +veins (SSJ4/Legendary/dark)
     -> divine/serene/still-water signatures (God/Blue/Rose/UI) -> +particles.
   The FLAT tint + a STATIC aura ring are always shown (an informative read); ALL
   motion is gated on .play.fx, so the FX toggle AND prefers-reduced-motion (the
   belt at the bottom) silence it. Unknown/custom (editor) forms get the graceful
   default aura built from their own color. Kaioken (Part 3) stacks on top.
   ========================================================================= */
.stat-form { font-family: var(--mono); font-size: 0.72em; letter-spacing: 0.08em; color: var(--form-color, var(--fg-faint)); text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; text-align: right; }
.play.fx .pr-stats.has-form .stat-form { text-shadow: 0 0 6px var(--form-color); }
/* KAIOKEN state chip (informative flat read — survives FX-off / reduced-motion) */
.stat-k { display: none; }
.pr-stats.has-k .stat-k {
  display: inline-block; font-family: var(--mono); font-size: 0.62em; letter-spacing: 0.12em;
  color: #ff6a6a; text-transform: uppercase; padding: 0 0.45ch; margin-right: 0.5ch; flex: none;
  border: 1px solid color-mix(in srgb, #ff5a5a 55%, transparent); border-radius: 2px;
}
.play.fx .pr-stats.has-k .stat-k { text-shadow: 0 0 6px rgba(255, 80, 80, 0.85); }

/* flat tint (always in cockpit): the form color themes the PL fill + border. Higher specificity than
   .stat-fill.warn/.crit so the form color wins the PL row (EN untouched). */
.play.cockpit .pr-stats.has-form .pl-bar { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--form-color) 72%, var(--line-hot)), inset 0 1px 0 rgba(255, 255, 255, 0.12); }
.play.cockpit .pr-stats.has-form .pl-bar .stat-fill { --fill: var(--form-color); }
/* in a form, the PL fill's own low-HP opacity pulse yields to the aura (one calm rhythm, not two) */
.play.fx .pr-stats.has-form .pl-bar .stat-fill { animation: none; }

/* the effect overlay stack (built once in play.js: buildStatFx). Bleeds a few px outside the track so
   the aura / arcs / motes can crawl around it; pointer-inert, transform/opacity/filter only. */
.stat-fx { position: absolute; inset: -4px; z-index: 4; pointer-events: none; overflow: visible; }
.stat-fx > * { position: absolute; inset: 0; pointer-events: none; }
.fx-aura, .fx-bolts, .fx-veins, .fx-motes, .fx-kaioken { opacity: 0; }

/* --- AURA: a soft form-colored halo (STATIC ring survives FX-off) + a licking flame edge (FX-only) --- */
.pr-stats.has-form .stat-fx .fx-aura {
  opacity: 1; border-radius: 3px;
  box-shadow:
    0 0 calc(4px + var(--fx-pow, 1) * 2px) color-mix(in srgb, var(--fx-c1) 42%, transparent),
    inset 0 0 4px color-mix(in srgb, var(--fx-c1) 28%, transparent);
}
.fx-aura::before {
  content: ''; position: absolute; left: 2px; right: 2px; top: -3px; height: 7px; opacity: 0;
  background: repeating-linear-gradient(90deg, transparent 0 3px,
    color-mix(in srgb, var(--fx-c1) 60%, transparent) 3px 5px, transparent 5px 8px);
  -webkit-mask-image: linear-gradient(180deg, #000, transparent);
  mask-image: linear-gradient(180deg, #000, transparent);
  filter: blur(0.5px);
}
.pr-stats.has-form .fx-aura::before { opacity: 0.5; }
.play.fx .pr-stats.has-form .fx-aura { animation: dbzAuraPulse 2.1s ease-in-out infinite; }
.play.fx .pr-stats.has-form .fx-aura::before { animation: dbzFlameLick 0.9s steps(4) infinite; }
@keyframes dbzAuraPulse {
  0%, 100% { box-shadow: 0 0 calc(4px + var(--fx-pow, 1) * 2px) color-mix(in srgb, var(--fx-c1) 40%, transparent), inset 0 0 4px color-mix(in srgb, var(--fx-c1) 25%, transparent); }
  50% { box-shadow: 0 0 calc(7px + var(--fx-pow, 1) * 3px) color-mix(in srgb, var(--fx-c1) 62%, transparent), inset 0 0 6px color-mix(in srgb, var(--fx-c1) 42%, transparent); }
}
@keyframes dbzFlameLick { from { transform: translateX(0); } to { transform: translateX(-8px); } }

/* --- ELECTRICITY (SSJ2+): JAGGED, SPORADIC lightning bolts struck by the JS striker (play.js fireBolt) ----
   The overlay is registered EXACTLY to the PL-bar box: .stat-fx bleeds -4px so the aura/veins can crawl
   just outside the track, but the bolt layer is pulled the SAME 4px BACK so it sits ON the gauge (above the
   fill, below no text — the overlay is aria-hidden/pointer-inert), tracking every gauge width on resize.
   The striker injects short-lived <g.fx-strike> bolts — each a soft wide colored GLOW path under a bright
   thin near-white CORE — that FLASH via dbzBoltFlash (a hard strobe) and are then culled, so it reads as
   erratic SSJ2 arcs, NOT a smooth turbulence shimmer. Motion is JS + .play.fx-gated; motion-off (reduced-
   motion / FX toggle) silences the striker and leaves a CALM STATIC GLOW. Tier is driven from JS (bolt
   count + gap timing) and here (WILD = thicker/hotter/brighter bolts). */
/* The bolt SVG is a REPLACED element: with only insets + a viewBox it takes an aspect-ratio height
   (viewBox 100:24 x the bar width) => a ~60-150px slab that dangles far below the ~10px bar. So we pin
   it to a DEFINITE thin band, centered on the bar: height = the bar box + ~2px bleed each side, so the
   24-unit viewBox maps onto ~bar height and the bolts crackle ALONG the track (a touch past top/bottom),
   never a slab below it. stat-fx bleeds -4px, so 100% here = bar + 8px; trimming 4px => bar + ~4px. */
.fx-bolts { inset: auto; left: 4px; top: 50%; width: calc(100% - 8px); height: calc(100% - 4px); transform: translateY(-50%); overflow: visible; }
.pr-stats.f-elec .stat-fx .fx-bolts { opacity: 1; }
/* CALM STATIC GLOW — the electric read when motion is OFF (reduced-motion / FX toggle / classic). .play.fx
   removes it so the live bolts carry the look and we never double up. */
.pr-stats.f-elec .fx-bolts { border-radius: 2px; box-shadow: inset 0 0 5px color-mix(in srgb, var(--fx-c1) 26%, transparent); }
.play.fx .pr-stats.f-elec .fx-bolts { box-shadow: none; }
/* each struck bolt: a soft wide colored GLOW under a bright thin near-white CORE (SSJ gold / SSJB blue per
   the form's --fx-c1/--fx-c2). Non-scaling stroke keeps the line weight crisp at any bar width. */
.fx-bolts .fx-bolt-glow {
  fill: none; stroke: var(--fx-c1); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke; opacity: 0.5; filter: drop-shadow(0 0 1.4px var(--fx-c1));
}
.fx-bolts .fx-bolt-core {
  fill: none; stroke: color-mix(in srgb, var(--fx-c2) 42%, #fff); stroke-width: 0.9; stroke-linecap: round;
  stroke-linejoin: round; vector-effect: non-scaling-stroke;
}
/* WILD tiers (SSJ3/SSJ4/Broly): hotter, thicker, brighter bolts (JS also strikes more of them, faster) */
.pr-stats.f-wild .fx-bolts .fx-bolt-glow { stroke-width: 3.2; opacity: 0.62; }
.pr-stats.f-wild .fx-bolts .fx-bolt-core { stroke-width: 1.2; }
/* THE FLICKER (FX-only): a hard strobe — snap ON, stutter, snap OFF — over each bolt's own JS-set duration
   (~80-340ms). steps(1,end) = no interpolation between keys, so it crackles rather than fades. Runs once,
   then the JS culls the node. When motion is off no .fx-strike nodes are ever spawned, so nothing animates. */
.play.fx .fx-bolts .fx-strike {
  animation-name: dbzBoltFlash; animation-timing-function: steps(1, end);
  animation-iteration-count: 1; animation-fill-mode: forwards;
}
@keyframes dbzBoltFlash {
  0%   { opacity: 0; }
  6%   { opacity: 1; }
  24%  { opacity: 0.28; }
  40%  { opacity: 1; }
  58%  { opacity: 0.5; }
  74%  { opacity: 1; }
  88%  { opacity: 0.32; }
  100% { opacity: 0; }
}

/* --- VEINS (SSJ4 / Legendary / dark forms): branching arcs pulsing along the bar --- */
.pr-stats.f-vein .stat-fx .fx-veins { opacity: 1; }
.fx-veins path {
  fill: none; stroke: var(--fx-vein, var(--fx-c2)); stroke-width: 1.4; stroke-linecap: round;
  vector-effect: non-scaling-stroke; opacity: 0.28; filter: drop-shadow(0 0 2px var(--fx-vein, var(--fx-c2)));
}
.play.fx .pr-stats.f-vein .fx-veins path { animation: dbzVein 1.9s ease-in-out infinite; }
.play.fx .pr-stats.f-vein .fx-veins path:nth-child(2) { animation-delay: 0.6s; }
.play.fx .pr-stats.f-vein .fx-veins path:nth-child(3) { animation-delay: 1.1s; }
@keyframes dbzVein { 0%, 100% { opacity: 0.22; } 50% { opacity: 0.85; } }

/* --- PARTICLES (God / Blue / Rose / UI / Namek): drifting motes rising off the gauge --- */
.pr-stats.f-particle .stat-fx .fx-motes { opacity: 1; }
.fx-motes::before, .fx-motes::after {
  content: ''; position: absolute; left: -2px; right: -2px; top: -4px; bottom: -4px; opacity: 0;
  background-image:
    radial-gradient(color-mix(in srgb, var(--fx-c1) 90%, #fff) 0.8px, transparent 1.5px),
    radial-gradient(color-mix(in srgb, var(--fx-c2) 80%, #fff) 0.8px, transparent 1.5px);
  background-size: 26px 22px, 40px 30px; background-position: 4px 8px, 15px 2px;
}
/* motes are pure motion (drifting sparks) — only shown under .play.fx, so motion-off leaves no frozen dots */
.play.fx .pr-stats.f-particle .fx-motes::before { opacity: 0.5; animation: dbzMoteRise 4.2s linear infinite; }
.play.fx .pr-stats.f-particle .fx-motes::after { opacity: 0.32; animation: dbzMoteRise 6.4s linear infinite reverse; }
@keyframes dbzMoteRise { from { transform: translateY(7px); } to { transform: translateY(-11px); } }
/* UI "still water": calmer, silvered drift + a slower, gentler aura (elegant, minimal-but-powerful) */
.play.fx .pr-stats.f-ui .fx-aura { animation-duration: 3.4s; }
.play.fx .pr-stats.f-ui .fx-motes::before { animation-duration: 6.5s; }
.play.fx .pr-stats.f-ui .fx-motes::after { animation-duration: 9s; }

/* --- KAIOKEN (Part 3): the iconic RED flare, STACKED on the base form aura, ESCALATING with level -------
   applyKaioken maps the server's x1..x20 to --k-int (0..1, EASE-IN so the top end pops) + a panel TIER
   class (k-low/k-mid/k-high/k-max). Low = a soft controlled red aura; each tier dials up glow + crackle
   speed and adds layers -- sparks (mid+), a pulsing cockpit RED VIGNETTE + rising embers (high/max), a
   near-constant hard pulse (max). All motion is .play.fx-gated + silenced by reduced-motion; the flat red
   ring + "KAIOKEN xN" chip stay when motion is off. Stacks on top of the base form aura. */
.pr-stats.has-k .stat-fx .fx-kaioken {
  opacity: 1; border-radius: 3px;
  box-shadow:
    0 0 calc(4px + var(--k-int, 0.3) * 22px) rgba(255, 52, 42, calc(0.3 + var(--k-int, 0.3) * 0.6)),
    inset 0 0 5px rgba(255, 70, 60, 0.42);
}
/* rising red heat wisps (the "crackle") — hotter/denser with level via --k-int */
.fx-kaioken::before {
  content: ''; position: absolute; left: -2px; right: -2px; top: -5px; bottom: -1px; opacity: 0;
  background: repeating-linear-gradient(84deg, transparent 0 5px,
    rgba(255, 72, 56, calc(0.2 + var(--k-int, 0.3) * 0.45)) 5px 7px, transparent 7px 12px);
  -webkit-mask-image: linear-gradient(180deg, #000, transparent 82%);
  mask-image: linear-gradient(180deg, #000, transparent 82%);
  filter: blur(0.7px);
}
.pr-stats.has-k .fx-kaioken::before { opacity: 0.62; }
/* red sparks flickering around the bar — appear at mid, intensify at high/max */
.fx-kaioken::after {
  content: ''; position: absolute; inset: -3px; opacity: 0;
  background-image:
    radial-gradient(rgba(255, 130, 95, 0.95) 0.7px, transparent 1.3px),
    radial-gradient(rgba(255, 74, 52, 0.9) 0.7px, transparent 1.3px);
  background-size: 15px 13px, 23px 19px; background-position: 2px 3px, 9px 1px;
}
/* spark visibility is FX-gated (opacity + animation both under .play.fx) so effects-off shows no frozen dots */
.play.fx.k-mid .pr-stats.has-k .fx-kaioken::after { opacity: 0.45; }
.play.fx.k-high .pr-stats.has-k .fx-kaioken::after { opacity: 0.6; }
.play.fx.k-max .pr-stats.has-k .fx-kaioken::after { opacity: 0.8; }

/* motion (FX-only): pulse + lick + sparks; speed scales with --k-int (ease-in) and tier */
.play.fx .pr-stats.has-k .fx-kaioken { animation: dbzKaioPulse calc(1.7s - var(--k-int, 0.3) * 1s) ease-in-out infinite; }
.play.fx .pr-stats.has-k .fx-kaioken::before { animation: dbzKaioLick calc(0.9s - var(--k-int, 0.3) * 0.55s) steps(4) infinite; }
.play.fx.k-mid .pr-stats.has-k .fx-kaioken::after,
.play.fx.k-high .pr-stats.has-k .fx-kaioken::after,
.play.fx.k-max .pr-stats.has-k .fx-kaioken::after { animation: dbzKaioSpark 0.5s steps(2, end) infinite; }
.play.fx.k-high .pr-stats.has-k .fx-kaioken::after { animation-duration: 0.34s; }
.play.fx.k-max .pr-stats.has-k .fx-kaioken::after { animation-duration: 0.22s; }
/* max: the hardest bar pulse (wider brightness swing) */
.play.fx.k-max .pr-stats.has-k .fx-kaioken { animation-name: dbzKaioPulseHard; }
/* the base form-glow pulse QUICKENS under Kaioken (only when stacked on a form) */
.play.fx.k-mid .pr-stats.has-form .fx-aura { animation-duration: 1.4s; }
.play.fx.k-high .pr-stats.has-form .fx-aura { animation-duration: 1s; }
.play.fx.k-max .pr-stats.has-form .fx-aura { animation-duration: 0.7s; }
@keyframes dbzKaioPulse { 0%, 100% { filter: brightness(0.82); } 50% { filter: brightness(1.34); } }
@keyframes dbzKaioPulseHard { 0%, 100% { filter: brightness(0.7); } 50% { filter: brightness(1.72); } }
@keyframes dbzKaioLick { from { transform: translateX(0); } to { transform: translateX(-10px); } }
@keyframes dbzKaioSpark {
  0%, 100% { opacity: 0.25; background-position: 2px 3px, 9px 1px; }
  50% { opacity: 0.78; background-position: 3px -3px, 8px -4px; }
}

/* HIGH/MAX: a pulsing RED VIGNETTE on the cockpit edges (the dbzEdge* edge-flash vocab, grown into a loop)
   + faint rising embers. Panel-level, cockpit-only (.play.fx implies cockpit). GPU-cheap: the vignette is a
   STATIC inset box-shadow whose OPACITY pulses (a composite, not a per-frame box-shadow repaint). .play::
   before is the corner-tick frame, so the vignette rides ::after and the embers ride a dedicated element. */
.play.fx.k-high::after, .play.fx.k-max::after {
  content: ''; position: absolute; inset: 0; z-index: 15; pointer-events: none;
  box-shadow: inset 0 0 42px 3px rgba(255, 44, 32, 0.30), inset 0 0 95px rgba(255, 30, 22, 0.16);
  animation: dbzKaioVig 1.1s ease-in-out infinite;
}
.play.fx.k-max::after {
  box-shadow: inset 0 0 58px 7px rgba(255, 44, 32, 0.46), inset 0 0 130px rgba(255, 24, 16, 0.26);
  animation: dbzKaioVigMax 0.7s ease-in-out infinite;
}
@keyframes dbzKaioVig { 0%, 100% { opacity: 0.32; } 50% { opacity: 1; } }
@keyframes dbzKaioVigMax { 0%, 100% { opacity: 0.62; } 50% { opacity: 1; } }
.k-embers { display: none; }
.play.fx.k-high .k-embers, .play.fx.k-max .k-embers {
  display: block; position: absolute; left: 0; right: 0; bottom: 0; height: 46%; z-index: 14; pointer-events: none;
  background-image:
    radial-gradient(rgba(255, 88, 56, 0.85) 0.9px, transparent 1.7px),
    radial-gradient(rgba(255, 60, 40, 0.7) 0.9px, transparent 1.7px);
  background-size: 40px 58px, 64px 86px; background-position: 0 0, 20px 0;
  -webkit-mask-image: linear-gradient(0deg, #000, transparent 88%);
  mask-image: linear-gradient(0deg, #000, transparent 88%);
  opacity: 0.4; animation: dbzKaioEmber 3.4s linear infinite;
}
.play.fx.k-max .k-embers { opacity: 0.6; animation-duration: 2.2s; }
@keyframes dbzKaioEmber {
  from { background-position: 0 58px, 20px 86px; }
  to { background-position: 0 0, 20px 0; }
}

/* ---- NEARBY pane — a scouter target roster -------------------------------------
   Each entity is a target-lock chip: corner brackets (the drawer's tick language),
   mono type, and a hostility-coded --tgt color (ally green / staff gold / neutral
   amber / HOSTILE red). A faint scan line textures the container. Click/menu
   behavior is untouched (the chips are the same buttons play.js wires up). */
.pr-nearby .nb-wrap {
  padding: 7px 8px; display: flex; flex-wrap: wrap; gap: 5px; min-height: 1.7em; align-content: flex-start;
  background-image: repeating-linear-gradient(0deg, transparent 0 3px, rgba(255, 255, 255, 0.014) 3px 4px);
}
/* (Zhayl) nearby chip: the NAME carries the mob's {X race/form color inline (server-sent); the
   .player/.staff/.npc/.hostile classes set --tgt (the target-lock color) for the frame + corner brackets. */
.nb-chip {
  --tgt: var(--fg-dim);
  position: relative; display: inline-flex; align-items: center; gap: 0.45ch;
  font-family: var(--term-font, var(--mono)); font-size: 0.86em; letter-spacing: 0.03em; line-height: 1.3;
  padding: 2px 9px; cursor: pointer; user-select: none; border-radius: 2px;
  border: 1px solid color-mix(in srgb, var(--tgt) 42%, transparent); color: var(--fg-dim);
  background: linear-gradient(180deg, color-mix(in srgb, var(--tgt) 8%, transparent), transparent),
              color-mix(in srgb, var(--bg-raise) 38%, transparent);
  transition: background 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
}
/* corner brackets — top-left + bottom-right L-ticks (the .pr-card corner-tick vocabulary), tinted --tgt */
.nb-chip::after {
  content: ''; position: absolute; inset: 1px; pointer-events: none;
  background-image:
    linear-gradient(var(--tgt), var(--tgt)), linear-gradient(var(--tgt), var(--tgt)),
    linear-gradient(var(--tgt), var(--tgt)), linear-gradient(var(--tgt), var(--tgt));
  background-repeat: no-repeat;
  background-position: left top, left top, right bottom, right bottom;
  background-size: 5px 1px, 1px 5px, 5px 1px, 1px 5px;
  opacity: 0.55; transition: opacity 120ms ease;
}
.nb-chip .ct { /* the "(N)" stack count — a small target-colored pill before the name */
  color: var(--tgt); font-weight: 700; font-size: 0.9em;
}
.nb-chip .ct:empty { display: none; }
.nb-chip .nm { white-space: nowrap; }
.nb-chip.player { --tgt: var(--green); }        /* ally */
.nb-chip.staff  { --tgt: #ffc24b; }             /* staff / deity */
.nb-chip.npc    { --tgt: #ff9a3c; }             /* neutral creature (amber) */
.nb-chip.neutral{ --tgt: var(--fg-faint); }
.nb-chip.hostile{ --tgt: #ff5a5a; }             /* actively hostile (red) */
.nb-chip:hover {
  background: linear-gradient(180deg, color-mix(in srgb, var(--tgt) 18%, transparent), transparent), var(--bg-raise);
  border-color: var(--tgt); box-shadow: 0 0 6px color-mix(in srgb, var(--tgt) 30%, transparent);
}
.nb-chip:hover::after { opacity: 1; }
.nb-none { color: var(--fg-faint); font-style: italic; font-size: 0.75em; }

/* ---- EXITS pane — one compact line: an "EXITS" label + the room's passable directions as
   understated clickable words (like the look command's exits line), updated live off the same
   `exits` frame data the compass uses. No chunky buttons — terminal-green words that brighten on
   hover and move you on click. ------------------------------------------------------------------ */
.pr-exits { flex: none; }
.pr-exits .ex-line {
  display: flex; align-items: center; flex-wrap: wrap; gap: 5px 0.7ch;
  padding: 6px 9px 8px; min-height: 1.4em;
}
.ex-label {
  flex: none; font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.16em;
  color: var(--fg-faint); text-transform: uppercase; user-select: none;
}
.ex-wrap {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: 5px; min-width: 0;
}
/* uniform all-caps direction BUTTONS — same colour throughout; hover just brightens the frame/fill,
   the text stays the terminal accent so they read as one consistent set. */
.ex-chip {
  font-family: var(--term-font, var(--mono)); font-size: 0.68rem; letter-spacing: 0.1em;
  text-transform: uppercase; cursor: pointer; line-height: 1;
  color: var(--term-accent, var(--green));
  background: color-mix(in srgb, var(--term-accent, var(--green)) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--term-accent, var(--green)) 42%, transparent);
  border-radius: 3px; padding: 4px 9px;
  transition: background 100ms ease, border-color 100ms ease;
}
.ex-chip:hover, .ex-chip:focus-visible {
  background: color-mix(in srgb, var(--term-accent, var(--green)) 22%, transparent);
  border-color: var(--term-accent, var(--green)); outline: none;
}
.ex-none { color: var(--fg-faint); font-style: italic; font-size: 0.72rem; letter-spacing: 0.08em; }

/* nearby quick menu (look / sense / punch — TYPES into the input, never sends) */
.qmenu {
  position: absolute; z-index: 8; min-width: 11rem;
  background: var(--bg-win); border: 1px solid var(--line-lock);
  box-shadow: 0 0.5em 1.4em rgba(0, 0, 0, 0.6);
  font-family: var(--mono); font-size: 0.78em;
}
.qmenu .qm-name { padding: 0.3rem 0.6rem; color: var(--amber); border-bottom: 1px solid var(--line); letter-spacing: 0.06em; }
.qmenu .qm-pl { padding: 0.24rem 0.6rem; color: var(--green); border-bottom: 1px solid var(--line); font-size: 0.9em; }
.qmenu .qm-it { padding: 0.28rem 0.6rem; color: var(--fg-faint); cursor: pointer; }
.qmenu .qm-it:hover { background: var(--bg-raise); color: var(--fg-dim); }
.qmenu .qm-it b { color: var(--term-accent, var(--green)); font-weight: 400; }

/* ---- CHAT pane -------------------------------------------------------------------- */
/* (Zhayl) flex-basis 0 (not auto): the chat card splits the rail by its GROW ratio, not its content -- so a
   growing chat log scrolls inside .pr-chatlog instead of expanding the card and squishing the map. */
.pr-chat { flex: 1 1 0; min-height: 6rem; }
.pr-ctabs { display: flex; gap: 2px; padding: 3px 6px 0; border-bottom: 1px solid var(--line); flex: none; }
.pr-ctab {
  display: inline-flex; align-items: center; gap: 0.6ch;
  font-family: var(--mono); font-size: 0.66em; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 2px 8px; cursor: pointer; border-radius: 0;
  color: var(--fg-faint); background: transparent; border: 1px solid transparent; border-bottom: none;
}
.pr-ctab:hover { color: var(--fg-dim); }
.pr-ctab.on { color: var(--term-accent, var(--green)); border-color: var(--line); }
.ct-badge {
  display: none; min-width: 1.4em; padding: 0 3px; font-size: 0.85em; text-align: center;
  background: var(--orange); color: #1a0e00;
}
.ct-badge.show { display: inline-block; }
.pr-chatlog {
  flex: 1; overflow-y: auto; padding: 4px 8px; min-height: 0;
  /* Zhayl: chat text follows the DISPLAY > size slider (like the output stream). The rail base is now fixed,
     so use --term-size directly (not an em of the rail) — a touch smaller than the main stream by default. */
  font-family: var(--term-font, var(--mono)); font-size: var(--term-size, clamp(14px, 1.1vw, 17px)); line-height: 1.4;
  white-space: pre-wrap; word-break: break-word; color: var(--term-fg, #f4f7fb);
}
.pr-chatlog[data-tab='ooc'] .tline:not([data-chan='ooc']),
.pr-chatlog[data-tab='say'] .tline:not([data-chan='say']),
.pr-chatlog[data-tab='tell'] .tline:not([data-chan='tell']) { display: none; }
.pr-chatlog .cempty { color: var(--fg-faint); font-style: italic; font-size: 0.85em; }
.pr-chatlog .chat-line { display: block; min-height: 0; margin: 0; padding: .12rem 0; }
.pr-chatlog .chat-line:empty { display: none; }

/* ============================================================================
   COCKPIT EFFECTS — all one-shots 100-300ms, transforms/opacity/box-shadow
   only, every rule gated on .play.fx (cockpit + effects toggle + no
   reduced-motion; JS enforces, the media query below is the second belt).
   ========================================================================= */

/* 1 — scouter boot: staggered pane power-on + one scan-line sweep */
.play.fx .play-rail.boot .pr-card { animation: dbzPrBoot 240ms ease-out backwards; }
.play.fx .play-rail.boot .pr-stats { animation-delay: 70ms; }
.play.fx .play-rail.boot .pr-nearby { animation-delay: 140ms; }
.play.fx .play-rail.boot .pr-chat { animation-delay: 210ms; }
@keyframes dbzPrBoot { from { opacity: 0; transform: translateY(4px); } }
.boot-scan { display: none; }
.play.fx .boot-scan {
  display: block; position: absolute; left: 0; right: 0; top: 0; height: 26%;
  pointer-events: none; z-index: 5;
  background: linear-gradient(180deg, transparent,
    color-mix(in srgb, var(--term-accent, #4df2a2) 14%, transparent), transparent);
  animation: dbzBootScan 300ms ease-out 1 forwards;
}
@keyframes dbzBootScan { from { transform: translateY(-100%); opacity: 1; } to { transform: translateY(420%); opacity: 0; } }

/* 2 — directional map wipe: DISABLED (Zhayl) — the fade-in (from opacity:0.55) + slide on EVERY step read
   as a distracting flicker/fadeout-fadein on the map. The wipe-* classes are still set by play.js but carry
   no animation now, so the map just updates instantly. (Keyframes kept below, unused, for easy revert.) */
@keyframes dbzWipeN  { from { transform: translateY(-10px); opacity: 0.55; } }
@keyframes dbzWipeS  { from { transform: translateY(10px); opacity: 0.55; } }
@keyframes dbzWipeE  { from { transform: translateX(10px); opacity: 0.55; } }
@keyframes dbzWipeW  { from { transform: translateX(-10px); opacity: 0.55; } }
@keyframes dbzWipeNE { from { transform: translate(7px, -7px); opacity: 0.55; } }
@keyframes dbzWipeNW { from { transform: translate(-7px, -7px); opacity: 0.55; } }
@keyframes dbzWipeSE { from { transform: translate(7px, 7px); opacity: 0.55; } }
@keyframes dbzWipeSW { from { transform: translate(-7px, 7px); opacity: 0.55; } }

/* 4 — damage edge flash (red = you were hit / died; gold = kill line) */
.play.fx .play-out.edge-red { animation: dbzEdgeR 220ms ease-out; }
.play.fx .play-out.edge-gold { animation: dbzEdgeG 260ms ease-out; }
@keyframes dbzEdgeR {
  from { box-shadow: inset 0 0 0 2px rgba(255, 90, 90, 0.85), inset 0 0 22px rgba(255, 90, 90, 0.28); }
  to { box-shadow: none; }
}
@keyframes dbzEdgeG {
  from { box-shadow: inset 0 0 0 2px rgba(255, 194, 75, 0.85), inset 0 0 22px rgba(255, 194, 75, 0.25); }
  to { box-shadow: none; }
}

/* 5 — transformation surge (SSJ gold / Legendary green / accent) */
.play.fx.surge .play-bar, .play.fx.surge .play-rail { animation: dbzSurge 300ms ease-out; }
@keyframes dbzSurge {
  from { box-shadow: inset 0 -1px 0 var(--fx-accent, #ffc24b), 0 0 14px var(--fx-accent, #ffc24b); }
}

/* 6 — nearby chip fades + hostile pulse */
.play.fx .nb-chip.chip-in { animation: dbzChipIn 150ms ease-out backwards; }
@keyframes dbzChipIn { from { opacity: 0; transform: scale(0.92); } }
.play.fx .nb-chip.chip-out { animation: dbzChipOut 180ms ease-out forwards; pointer-events: none; }
@keyframes dbzChipOut { to { opacity: 0; transform: scale(0.9); } }
.play.fx .nb-chip.hostile { animation: dbzChipHot 1.6s ease-in-out infinite; }
@keyframes dbzChipHot { 0%, 100% { opacity: 1; } 50% { opacity: 0.72; } }

/* 7 — area stamp: REMOVED. The on-map region name overlay (WEST/EAST/... printed big over the grid) was
   redundant with the top-left area label in the map header, so the stamp text + its border-pulse flash
   are gone. The top-left `hdArea` label remains the single source of the current region name. */

/* 8 — weather micro-effects on the map pane (barely-there, pointer-inert). The heavier "living
   weather" families + storm lightning shipped 2026-07-09 were REMOVED next day (owner: too noisy) —
   this is the original subtle read. */
.play.fx .pr-map.wx-rain .pr-fx::before {
  content: ''; position: absolute; left: 0; right: 0; top: -60%; bottom: -60%;
  background: repeating-linear-gradient(105deg, transparent 0 6px, rgba(91, 140, 255, 0.06) 6px 7px);
  animation: dbzWxRain 2.2s linear infinite;
}
@keyframes dbzWxRain { to { transform: translateY(30%); } }
.play.fx .pr-map.wx-snow .pr-fx::before,
.play.fx .pr-map.wx-snow .pr-fx::after {
  content: ''; position: absolute; left: 0; right: 0; top: -55%; bottom: 0;
  background-image:
    radial-gradient(rgba(255, 255, 255, 0.30) 1px, transparent 1.6px),
    radial-gradient(rgba(255, 255, 255, 0.18) 1px, transparent 1.6px);
  background-size: 34px 34px, 57px 57px;
  background-position: 3px 7px, 19px 23px;
  opacity: 0.32;
  animation: dbzWxSnow 3.4s linear infinite;
}
.play.fx .pr-map.wx-snow .pr-fx::after { animation-duration: 5s; opacity: 0.2; background-position: 12px 1px, 5px 40px; }
@keyframes dbzWxSnow { to { transform: translateY(38%); } }
.play.fx .pr-map.wx-heat .pr-fx::before {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(255, 140, 26, 0.05), rgba(255, 80, 40, 0.09));
  animation: dbzWxHeat 2.4s ease-in-out infinite;
}
@keyframes dbzWxHeat { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }
/* night dimming is a STATIC tint (survives reduced-motion by design) */
.play.fx .pr-map.wx-night .pr-fx { background-color: rgba(2, 6, 20, 0.22); }

/* Outer-space camera treatment — a restrained parallax starfield, faint violet/cyan nebula depth and
   an occasional distant streak. It sits in the pointer-inert FX plane above the void background but at
   deliberately low opacity, so planet/ship/pod glyphs remain the brightest and most readable objects. */
.play.fx .pr-map.wx-space .pr-fx {
  background:
    radial-gradient(ellipse 62% 46% at 18% 82%, rgba(78,74,180,.10), transparent 72%),
    radial-gradient(ellipse 55% 38% at 82% 18%, rgba(46,154,180,.075), transparent 75%),
    rgba(0,0,5,.10);
}
.play.fx .pr-map.wx-space .pr-fx::before {
  content: ''; position: absolute; inset: -18%; opacity: .34;
  background-image:
    radial-gradient(circle, rgba(255,255,255,.75) 0 1px, transparent 1.4px),
    radial-gradient(circle, rgba(137,205,255,.58) 0 1px, transparent 1.5px),
    radial-gradient(circle, rgba(218,188,255,.42) 0 .8px, transparent 1.3px);
  background-size: 43px 47px, 79px 73px, 127px 109px;
  background-position: 5px 9px, 31px 17px, 62px 48px;
  mix-blend-mode: screen;
  animation: dbzSpaceDrift 28s linear infinite;
}
.play.fx .pr-map.wx-space .pr-fx::after {
  content: ''; position: absolute; left: -32%; top: -25%; width: 38%; height: 1px;
  opacity: 0; transform: rotate(-24deg);
  background: linear-gradient(90deg, transparent, rgba(151,221,255,.18) 58%, rgba(255,255,255,.72));
  box-shadow: 0 0 5px rgba(103,203,255,.32);
  animation: dbzSpaceStreak 11s ease-in infinite;
}
@keyframes dbzSpaceDrift { to { transform: translate(5.5%, 3.5%); } }
@keyframes dbzSpaceStreak {
  0%, 68%, 100% { opacity: 0; translate: 0 0; }
  70% { opacity: .55; }
  77% { opacity: 0; translate: 360% 520%; }
}

/* 9 — otherworld mode: death desaturation + pale halo until the respawn redraw */
.play.cockpit { transition: filter 300ms ease; }
.play.otherworld { filter: saturate(0.35) brightness(0.92); --term-accent: #cfd8ff; }

/* 10 — scouter readout: targeting-bracket scan around the chip quick menu */
.play.fx .qmenu.scan { animation: dbzScanIn 280ms ease-out; }
@keyframes dbzScanIn { from { transform: scale(1.25); opacity: 0; } }
.play.fx .qmenu.scan::before, .play.fx .qmenu.scan::after {
  content: ''; position: absolute; width: 10px; height: 10px; pointer-events: none;
}
.play.fx .qmenu.scan::before {
  top: -4px; left: -4px;
  border-top: 1px solid var(--term-accent, #4df2a2); border-left: 1px solid var(--term-accent, #4df2a2);
}
.play.fx .qmenu.scan::after {
  bottom: -4px; right: -4px;
  border-bottom: 1px solid var(--term-accent, #4df2a2); border-right: 1px solid var(--term-accent, #4df2a2);
}

/* 11 — Dragon Radar map theme (pane-scoped skin; MAIN/chat colors untouched) */
.play.cockpit .pr-map.radar .pr-grid {
  background: radial-gradient(circle at 50% 45%, rgba(24, 255, 140, 0.07), rgba(2, 18, 8, 0.85));
  filter: sepia(1) hue-rotate(90deg) saturate(1.6);
}
.play.cockpit .pr-map.radar .pr-fx {
  background:
    linear-gradient(rgba(24, 255, 140, 0.05) 1px, transparent 1px) 0 0 / 100% 25%,
    linear-gradient(90deg, rgba(24, 255, 140, 0.05) 1px, transparent 1px) 0 0 / 25% 100%,
    radial-gradient(circle at 50% 50%, transparent 55%, rgba(2, 4, 3, 0.85) 96%);
}

/* 12 — idle sleep overlay (5 idle minutes; any key wakes it). The Dragon Radar rotor continues to
   use `.pr-sweep`; ordinary inactivity now reads as rest instead of an unexplained scanner firing. */
.pr-sleep { display: none; }
.play.fx .pr-sleep {
  display: block; position: absolute; inset: 0; z-index: 5; overflow: hidden; pointer-events: none;
  background: radial-gradient(circle at 54% 56%, rgba(9, 18, 28, .18), rgba(2, 5, 8, .48));
  animation: idleSleepVeil 700ms ease-out both;
}
.play.fx .pr-sleep span {
  position: absolute; left: 48%; top: 59%;
  color: #d9f1ff; font-weight: 800; line-height: 1; text-shadow: 0 0 6px rgba(127, 210, 255, .9);
  opacity: 0; transform: translate(0, 0) scale(.65) rotate(-8deg);
  animation: idleSleepZ 3.2s ease-out infinite;
}
.play.fx .pr-sleep span:nth-child(2) { animation-delay: .7s; font-size: 1.15em; }
.play.fx .pr-sleep span:nth-child(3) { animation-delay: 1.4s; font-size: .9em; }
.play.fx .pr-sleep span:nth-child(4) { animation-delay: 2.1s; font-size: 1.35em; }
@keyframes idleSleepVeil { from { opacity: 0; } to { opacity: 1; } }
@keyframes idleSleepZ {
  0% { opacity: 0; transform: translate(0, 5px) scale(.6) rotate(-10deg); }
  18% { opacity: .95; }
  72% { opacity: .68; }
  100% { opacity: 0; transform: translate(34px, -45px) scale(1.35) rotate(8deg); }
}

/* Dragon Radar rotor (theme-scoped, never triggered merely by idling). */
.pr-sweep { display: none; }
.play.fx .pr-sweep {
  display: block; position: absolute; inset: -30%; z-index: 3; pointer-events: none;
  background: conic-gradient(from 0deg, transparent 0 340deg, rgba(70, 245, 184, 0.5) 355deg, transparent 360deg);
  opacity: 0;
  animation: dbzSweepIn 300ms ease-out forwards, dbzSweepRot 3.5s linear infinite;
}
@keyframes dbzSweepIn { to { opacity: 0.12; } }
@keyframes dbzSweepRot { to { transform: rotate(1turn); } }

/* 13 — #438: eternal-dragon summon sky-darken (planet-scoped). The server sets .dragon (+ data-dragon =
   Shenron|Porunga) ONLY for players on the summon planet. Visual is darkness only — map dim + a soft
   top-weighted veil. Narrative text is the normal game lines ("The sky darkens...", summon yell); no
   decorative serpent or name banner. */
/* darken the map glyphs (the veil below adds the tint); static so reduced-motion/FX-off still darkens.
   The smooth fade is FX-only (transition lives under .play.fx), so reduced-motion darkens instantly. */
.play.cockpit.dragon .pr-grid { filter: brightness(0.72) saturate(0.85); }
.play.fx.dragon .pr-grid { transition: filter 600ms ease; }
.pr-dragon { display: none; }
.play.cockpit.dragon .pr-dragon {
  /* z-index 3: above the weather FX layer (z2) but BELOW the impair overlay (z4), so a KO/blind veil
     still dominates when both are active. */
  display: block; position: absolute; inset: 0; z-index: 3; pointer-events: none; overflow: hidden;
  /* the "sky darkens" veil over the map pane (top-weighted so lower rows stay readable) */
  background: linear-gradient(180deg, rgba(3, 6, 18, 0.72) 0%, rgba(5, 9, 26, 0.44) 45%, rgba(3, 6, 16, 0.30) 100%);
}

/* 15 — (T0.4) impact SHAKE on the map grid box: a one-shot decaying rattle (trauma-style falloff baked
   into the keyframes), amplitude from --shake-amp ('low' default; 'full' via .fx-shake-full). Only the
   MAP box ever shakes — never the terminal (reading scrolling text through a shake is nauseating). */
.play.fx { --shake-amp: 1.5px; }
.play.fx.fx-shake-full { --shake-amp: 4px; }
.play.fx .pr-gridwrap.shake-sm { animation: dbzShake 240ms linear 1; }
.play.fx .pr-gridwrap.shake-lg { animation: dbzShake 420ms linear 1; }
@keyframes dbzShake {
  8%  { transform: translate(var(--shake-amp), calc(var(--shake-amp) * -0.7)); }
  22% { transform: translate(calc(var(--shake-amp) * -0.9), calc(var(--shake-amp) * 0.5)); }
  38% { transform: translate(calc(var(--shake-amp) * 0.7), calc(var(--shake-amp) * 0.4)); }
  54% { transform: translate(calc(var(--shake-amp) * -0.5), calc(var(--shake-amp) * -0.3)); }
  72% { transform: translate(calc(var(--shake-amp) * 0.25), calc(var(--shake-amp) * 0.2)); }
  88% { transform: translate(calc(var(--shake-amp) * -0.1), 0); }
  100% { transform: translate(0, 0); }
}

/* 16 — (T0.5) dragon-summon STORM: slow summon lightning flickering through the sky-darken veil, cast
   in the dragon's color (Shenron green / Porunga crimson). Dim (peaks 0.2 alpha) + rare (one flicker
   cluster per ~7s) — flash-safe. The static veil (section 13) is the reduced-motion read. */
.play.cockpit[data-dragon='Shenron'] { --drag-c: #58ff9c; }
.play.cockpit[data-dragon='Porunga'] { --drag-c: #ff6a5a; }
.play.fx.fxo-drag.dragon .pr-dragon::before {
  content: ''; position: absolute; inset: 0; opacity: 0;
  background: linear-gradient(195deg, color-mix(in srgb, var(--drag-c, #58ff9c) 30%, transparent), transparent 60%);
  animation: dbzDragFlick 7.3s steps(1, end) infinite;
}
@keyframes dbzDragFlick {
  0%, 58%, 100% { opacity: 0; }
  59% { opacity: 0.16; }
  61% { opacity: 0.04; }
  64% { opacity: 0.2; }
  68% { opacity: 0; }
}
/* a faint standing cast of the dragon's color low on the veil (static — part of the darkness read) */
.play.cockpit.fxo-drag.dragon .pr-dragon::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(ellipse 60% 40% at 50% 100%, color-mix(in srgb, var(--drag-c, #58ff9c) 10%, transparent), transparent 75%);
}

/* 17 — (T0.6) impairment TEXTURE on the veil — the pitch-black gate itself is 1:1 (you perceive
   nothing; only the label shows) and is NOT touched. Blind = signal-noise crawl, sleep = a slow
   breathing glow behind the label, KO = a static blood-dark collapse vignette. */
.play.fx.fxo-imp[data-impair='blind'] .pr-impair::before {
  content: ''; position: absolute; left: 0; right: 0; top: -30%; bottom: -30%;
  background-image:
    repeating-linear-gradient(0deg, transparent 0 2px, rgba(255, 255, 255, 0.05) 2px 3px, transparent 3px 5px),
    repeating-linear-gradient(90deg, transparent 0 3px, rgba(255, 255, 255, 0.03) 3px 4px);
  animation: dbzImpairNoise 700ms steps(6) infinite;
}
@keyframes dbzImpairNoise { to { transform: translateY(8%); } }
.play.fx.fxo-imp[data-impair='sleep'] .pr-impair::before {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(ellipse 55% 32% at 50% 50%, rgba(120, 140, 255, 0.13), transparent 72%);
  animation: dbzImpairBreath 4.5s ease-in-out infinite;
}
@keyframes dbzImpairBreath { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
.play.cockpit.fxo-imp[data-impair='ko'] .pr-impair { box-shadow: inset 0 0 60px 6px rgba(120, 10, 10, 0.55); }

/* 18 — (T0.7) Dragon Radar ROTOR: the always-on slow sweep while the radar theme + sweep toggle are
   live (play.js mounts/unmounts it in applyAll). Same conic as the idle sweep, calmer cadence. */
.play.fx .pr-sweep.rotor { animation: dbzSweepIn 300ms ease-out forwards, dbzSweepRot 6.5s linear infinite; }

/* 19 — (MAP FX X.1) one-shot COMBAT FX from the state frame's `fx` array: expanding shockwave RINGS
   (ki = warm orange, or the technique's own pColor when the server sends one; block = cyan; impact =
   white; powerup = gold flare), the form-colored transform BURST, and the death SOUL-rise. All
   transform/opacity one-shots positioned on the fitted cell grid (mapFxEvent), self-culled on
   animationend, pool-capped at 6 — and all dead under the damage-flash sub-toggle. */
.pr-ring, .pr-burst, .pr-soul, .pr-aura-shock { display: none; }
.play.fx.fxo-flash .pr-ring {
  display: block; position: absolute; z-index: 2; pointer-events: none; border-radius: 50%;
  border: 2px solid var(--ring-c, #f4f7fb);
  box-shadow: 0 0 8px var(--ring-c, #f4f7fb), inset 0 0 6px color-mix(in srgb, var(--ring-c, #f4f7fb) 55%, transparent);
  opacity: 0; transform: scale(0.15);
  animation: dbzRing 480ms cubic-bezier(0.16, 0.84, 0.44, 1) 1 forwards;
}
/* Every impact ring now has a compressed core bloom and a delayed pressure shell. They inherit the
   event tint and intensity-sized parent box, so existing blocks, transformations, pod hits and ki
   explosions gain depth without adding server events or permanent DOM. Text/glyphs remain untouched. */
.play.fx.fxo-flash .pr-ring::before,
.play.fx.fxo-flash .pr-ring::after {
  content: ''; position: absolute; pointer-events: none; border-radius: 50%;
}
.play.fx.fxo-flash .pr-ring::before {
  inset: 39%;
  background: radial-gradient(circle, #fff 0 12%, color-mix(in srgb, var(--ring-c, #f4f7fb) 88%, white) 28%, var(--ring-c, #f4f7fb) 48%, transparent 74%);
  filter: drop-shadow(0 0 7px var(--ring-c, #f4f7fb));
  animation: dbzImpactCore 430ms ease-out 1 forwards;
}
.play.fx.fxo-flash .pr-ring::after {
  inset: 8%; border: 1px solid color-mix(in srgb, var(--ring-c, #f4f7fb) 72%, white);
  box-shadow: 0 0 9px color-mix(in srgb, var(--ring-c, #f4f7fb) 60%, transparent);
  opacity: 0; transform: scale(.08);
  animation: dbzImpactShell 680ms cubic-bezier(.08,.72,.2,1) 90ms 1 forwards;
}
.play.fx.fxo-flash .pr-ring.ring-block::before { opacity: .55; }
.play.fx.fxo-flash .pr-ring.ring-block::after { animation-duration: 430ms; }
@keyframes dbzImpactCore {
  0% { transform: scale(.1); opacity: 1; }
  38% { transform: scale(3.1); opacity: .88; }
  100% { transform: scale(5.2); opacity: 0; }
}
@keyframes dbzImpactShell {
  0% { transform: scale(.08); opacity: .82; }
  100% { transform: scale(1.15); opacity: 0; }
}
.pr-ring.ring-ki { --ring-c: #ff9a3c; }       /* warm ki default; a sent pColor overrides inline */
.pr-ring.ring-block { --ring-c: #6ee9f0; }    /* cyan deflect */
.pr-ring.ring-impact { --ring-c: #f4f7fb; }   /* white body-slam / heavy hit */
.pr-ring.ring-powerup { --ring-c: #ffc24b; animation-duration: 650ms; }
.pr-ring.ring-dust { --ring-c: #c9b98a; animation-duration: 420ms; } /* (SP.4) fling landing thud */
.pr-ring.ring-pod { --ring-c: #ffcf68; border-width: 3px; animation-duration: 760ms; }
@keyframes dbzRing {
  0% { transform: scale(0.15); opacity: 0.9; }
  70% { opacity: 0.45; }
  100% { transform: scale(1); opacity: 0; }
}

/* Redirected energy: a white-hot comet races from the defender to the server-selected world impact,
   followed by a layered DBZ explosion. The wrapper clips with the map pane and never catches input. */
.pr-deflect { position: absolute; inset: 0; z-index: 5; pointer-events: none; overflow: visible; --deflect-c: #6ee9f0; }
.pr-deflect > i, .pr-deflect-impact > b { position: absolute; display: block; pointer-events: none; }
.pr-deflect-ray {
  height: 4px; margin-top: -2px; transform-origin: left center; border-radius: 999px;
  background: linear-gradient(90deg, transparent 0, color-mix(in srgb, var(--deflect-c) 58%, transparent) 18%, #fff 72%, var(--deflect-c) 100%);
  filter: drop-shadow(0 0 5px var(--deflect-c));
  clip-path: inset(0 100% 0 0);
  animation: dbzDeflectRay 420ms cubic-bezier(.15,.72,.28,1) 1 forwards;
}
.pr-deflect-comet {
  width: 10px !important; height: 10px; margin: -5px 0 0 -5px; transform-origin: 5px 5px;
  border-radius: 50%; background: #fff;
  box-shadow: 0 0 5px 2px #fff, 0 0 14px 6px var(--deflect-c);
  offset-path: none;
  animation: dbzDeflectComet 420ms cubic-bezier(.15,.72,.28,1) 1 forwards;
}
.pr-deflect-impact { border-radius: 50%; animation: dbzDeflectImpact 1050ms ease-out 180ms 1 both; }
.pr-deflect-impact > b { inset: 0; border-radius: 50%; }
.pr-deflect-impact .di-core {
  inset: 31%; background: radial-gradient(circle, #fff 0 10%, color-mix(in srgb, var(--deflect-c) 86%, white) 28%, var(--deflect-c) 50%, transparent 74%);
  filter: drop-shadow(0 0 9px var(--deflect-c)); animation: dbzDeflectCore 580ms ease-out 1 forwards;
}
.pr-deflect-impact .di-wave { border: 2px solid var(--deflect-c); opacity: 0; transform: scale(.08); box-shadow: 0 0 10px var(--deflect-c); }
.pr-deflect-impact .di-wave-1 { border-color: #fff; animation: dbzDeflectWave 480ms cubic-bezier(.08,.74,.2,1) 1 forwards; }
.pr-deflect-impact .di-wave-2 { inset: 9%; animation: dbzDeflectWave 820ms cubic-bezier(.08,.74,.2,1) 80ms 1 forwards; }
.pr-deflect-impact .di-debris {
  inset: 25%; background:
    radial-gradient(circle at 10% 20%, #fff 0 1.5%, transparent 2%),
    radial-gradient(circle at 86% 28%, var(--deflect-c) 0 2%, transparent 2.7%),
    radial-gradient(circle at 22% 84%, #b98752 0 2%, transparent 2.8%),
    radial-gradient(circle at 78% 76%, #fff 0 1.4%, transparent 2.1%);
  animation: dbzDeflectDebris 760ms ease-out 1 forwards;
}
@keyframes dbzDeflectRay { 0% { clip-path: inset(0 100% 0 0); opacity: .15; } 58% { clip-path: inset(0 0 0 0); opacity: 1; } 100% { clip-path: inset(0 0 0 100%); opacity: 0; } }
@keyframes dbzDeflectComet { 0% { translate: 0 0; opacity: 1; scale: .65; } 82% { translate: var(--deflect-dx, 0px) var(--deflect-dy, 0px); opacity: 1; scale: 1.15; } 100% { translate: var(--deflect-dx, 0px) var(--deflect-dy, 0px); opacity: 0; scale: 1.8; } }
@keyframes dbzDeflectImpact { 0% { opacity: 0; } 8%,72% { opacity: 1; } 100% { opacity: 0; } }
@keyframes dbzDeflectCore { 0% { transform: scale(.08); opacity: 1; } 42% { transform: scale(3); opacity: .95; } 100% { transform: scale(5); opacity: 0; } }
@keyframes dbzDeflectWave { 0% { transform: scale(.08); opacity: .95; } 100% { transform: scale(1.1); opacity: 0; } }
@keyframes dbzDeflectDebris { 0% { transform: scale(.15) rotate(0); opacity: 1; } 100% { transform: scale(3.1) rotate(32deg); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .pr-deflect { display: none !important; }
  .play.fx .pr-map.wx-space .pr-fx::before,
  .play.fx .pr-map.wx-space .pr-fx::after { animation: none !important; }
  .play.fx .pr-map.wx-space .pr-fx::after { display: none; }
}
/* Summoned-pod atmospheric landing hit. Unlike positioned map FX this mounts on `.play`, so nearby
   observers still see the entry streak/flash/dust pressure wave in Classic mode or with MAP folded. */
.pr-pod-impact { position: absolute; inset: 0; z-index: 30; overflow: hidden; pointer-events: none; }
.pr-pod-impact > i { position: absolute; display: block; pointer-events: none; }
.pr-pod-impact .pod-body {
  left: 58%; top: -12%; width: 18px; height: 26px; margin-left: -9px; z-index: 2;
  border-radius: 55% 55% 42% 42%; background: radial-gradient(circle at 50% 35%, #fff, #d7e5eb 28%, #506b78 70%);
  box-shadow: 0 0 11px #ffd072; animation: dbzPodDescend 650ms cubic-bezier(.55,.02,.82,.42) forwards;
}
.pr-pod-impact .pod-streak {
  left: 52%; top: -35%; width: 5px; height: 82%; transform: rotate(24deg);
  background: linear-gradient(180deg, transparent, #fff 55%, #ffc45c 82%, transparent);
  filter: drop-shadow(0 0 9px #ff9a3c); animation: dbzPodStreak 520ms ease-in forwards;
}
.pr-pod-impact .pod-flash {
  inset: 0; background: radial-gradient(circle at 58% 72%, rgba(255,255,255,.92) 0 2%, rgba(255,177,65,.48) 4%, transparent 28%);
  mix-blend-mode: screen; animation: dbzPodFlash 820ms ease-out forwards;
}
.pr-pod-impact .pod-dust {
  left: 8%; right: 8%; bottom: -18%; height: 52%; border-radius: 50%;
  background: radial-gradient(ellipse, rgba(196,158,104,.48), rgba(104,75,48,.2) 38%, transparent 70%);
  filter: blur(5px); animation: dbzPodDust 1050ms ease-out forwards;
}
@keyframes dbzPodStreak { 0% { opacity: 0; transform: translate(-42vw,-20vh) rotate(24deg) scaleY(.3); } 20% { opacity: 1; } 100% { opacity: 0; transform: translate(0,58vh) rotate(24deg) scaleY(1); } }
@keyframes dbzPodDescend { 0% { opacity: 0; transform: translate(-30vw,-12vh) scale(.35) rotate(24deg); } 12% { opacity: 1; } 82% { opacity: 1; transform: translate(-2vw,72vh) scale(1) rotate(24deg); } 100% { opacity: 0; transform: translate(0,78vh) scale(.72) rotate(24deg); } }
@keyframes dbzPodFlash { 0%,35% { opacity: 0; transform: scale(.55); } 48% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1.35); } }
@keyframes dbzPodDust { 0%,40% { opacity: 0; transform: scale(.25); } 58% { opacity: .9; } 100% { opacity: 0; transform: scale(1.35) translateY(-12%); } }
.pr-pod-takeoff { position: absolute; inset: 0; z-index: 30; overflow: hidden; pointer-events: none; }
.pr-pod-takeoff > i { position: absolute; display: block; left: 50%; pointer-events: none; }
.pr-pod-takeoff .pto-pod { bottom: 18%; width: 18px; height: 26px; margin-left: -9px; border-radius: 55% 55% 42% 42%; background: radial-gradient(circle at 50% 35%, #fff, #b9d9e8 28%, #496675 70%); box-shadow: 0 0 10px #8cecff; animation: dbzPodLift 1050ms cubic-bezier(.25,.72,.32,1) forwards; }
.pr-pod-takeoff .pto-trail { bottom: 8%; width: 7px; height: 38%; margin-left: -3px; background: linear-gradient(0deg, transparent, #54dfff 28%, #fff 80%, transparent); filter: drop-shadow(0 0 8px #36cfff); transform-origin: bottom; animation: dbzPodTrail 1050ms ease-in forwards; }
.pr-pod-takeoff .pto-pop { top: 5%; width: 8px; height: 8px; margin-left: -4px; border-radius: 50%; background: #fff; box-shadow: 0 0 18px 8px #7feaff; opacity: 0; animation: dbzPodPop 1050ms ease-out forwards; }
@keyframes dbzPodLift { 0% { transform: translateY(0) scale(1); opacity: 1; } 68% { transform: translateY(-58vh) scale(.62); opacity: 1; } 88%,100% { transform: translateY(-82vh) scale(.12); opacity: 0; } }
@keyframes dbzPodTrail { 0% { opacity: 0; transform: scaleY(.05); } 18% { opacity: 1; } 75% { opacity: .9; transform: translateY(-40vh) scaleY(1.7); } 100% { opacity: 0; transform: translateY(-72vh) scaleY(.15); } }
@keyframes dbzPodPop { 0%,76% { opacity: 0; transform: scale(.2); } 86% { opacity: 1; transform: scale(1.4); } 100% { opacity: 0; transform: scale(3); } }
.pr-pod-path { position: absolute; z-index: 7; pointer-events: none; border-radius: 55% 55% 42% 42%;
  background: radial-gradient(circle at 50% 32%, #fff, #d7e5eb 30%, #496675 72%); box-shadow: 0 0 8px #7feaff; }
.pr-pod-path i { position: absolute; left: 35%; top: 78%; width: 30%; height: 150%;
  background: linear-gradient(180deg, #fff, #4bdcff 34%, transparent); filter: drop-shadow(0 0 5px #32cfff); }
.pr-pod-path.path-takeoff { animation: dbzPodMapTakeoff 900ms cubic-bezier(.2,.72,.28,1) forwards; }
.pr-pod-path.path-landing { animation: dbzPodMapLanding 820ms cubic-bezier(.58,.02,.82,.4) forwards; }
@keyframes dbzPodMapTakeoff { 0% { opacity: 1; transform: translate(0,0) scale(1); } 78% { opacity: 1; transform: translate(var(--pod-dx),var(--pod-dy)) scale(.45); } 100% { opacity: 0; transform: translate(var(--pod-dx),var(--pod-dy)) scale(.08); } }
@keyframes dbzPodMapLanding { 0% { opacity: 0; transform: translate(0,0) scale(.15); } 15% { opacity: 1; } 88% { opacity: 1; transform: translate(var(--pod-dx),var(--pod-dy)) scale(1); } 100% { opacity: 0; transform: translate(var(--pod-dx),var(--pod-dy)) scale(.78); } }
.play.fx.fxo-flash .pr-aura-shock {
  display: block; position: absolute; z-index: 3; pointer-events: none; border-radius: 50%;
  mix-blend-mode: screen; animation: dbzAuraShell 920ms ease-out 1 forwards;
}
.pr-aura-shock > i { position: absolute; inset: 0; display: block; border-radius: 50%; }
.pr-aura-shock .aura-core {
  inset: 36%;
  background: radial-gradient(circle, #fff 0 8%, color-mix(in srgb, var(--aura-c, #6ee9f0) 90%, white) 22%, var(--aura-c, #6ee9f0) 48%, transparent 72%);
  filter: blur(.5px) drop-shadow(0 0 7px var(--aura-c, #6ee9f0));
  animation: dbzAuraCore 620ms ease-out 1 forwards;
}
.pr-aura-shock .aura-wave {
  border: 2px solid var(--aura-c, #6ee9f0);
  box-shadow: 0 0 10px var(--aura-c, #6ee9f0), inset 0 0 8px color-mix(in srgb, var(--aura-c, #6ee9f0) 65%, transparent);
  opacity: 0; transform: scale(.08);
}
.pr-aura-shock .aw-1 { border-color: #fff; animation: dbzPressureWave 380ms cubic-bezier(.08,.72,.2,1) 1 forwards; }
.pr-aura-shock .aw-2 { animation: dbzPressureWave 620ms cubic-bezier(.08,.72,.2,1) 70ms 1 forwards; }
.pr-aura-shock .aw-3 { border-width: 1px; filter: blur(.8px); animation: dbzPressureWave 880ms cubic-bezier(.08,.72,.2,1) 120ms 1 forwards; }
@keyframes dbzAuraCore {
  0% { transform: scale(.15); opacity: 1; }
  38% { transform: scale(2.5); opacity: .9; }
  100% { transform: scale(4); opacity: 0; }
}
@keyframes dbzPressureWave {
  0% { transform: scale(.06); opacity: .95; }
  22% { opacity: .82; }
  100% { transform: scale(1); opacity: 0; }
}
@keyframes dbzAuraShell { 0%, 82% { opacity: 1; } 100% { opacity: 0; } }
.play.fx.fxo-flash .pr-burst {
  display: block; position: absolute; z-index: 2; pointer-events: none; border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, var(--burst-c, #ffc24b) 35%, transparent 70%);
  mix-blend-mode: screen; opacity: 0;
  animation: dbzBurst 700ms ease-out 1 forwards;
}
@keyframes dbzBurst {
  0% { transform: scale(0.2); opacity: 0.95; }
  40% { transform: scale(1); opacity: 0.7; }
  100% { transform: scale(1.3); opacity: 0; }
}
.play.fx.fxo-flash .pr-soul {
  display: block; position: absolute; z-index: 2; pointer-events: none; border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(140, 190, 255, 0.5) 45%, transparent 70%);
  animation: dbzSoul 900ms ease-out 1 forwards;
}
@keyframes dbzSoul {
  0% { transform: translateY(0) scale(0.5); opacity: 0; }
  25% { opacity: 0.8; }
  100% { transform: translateY(-220%) scale(0.9); opacity: 0; }
}

/* 20 — (SP.5 scouter) TARGET ACQUIRED: a one-shot red scan-line sweeping down the NEARBY pane when a
   hostile first locks on (markHostiles). Self-culled on animationend. */
.nb-scan { display: none; }
.play.fx.fxo-scout .nb-scan {
  display: block; position: absolute; left: 0; right: 0; top: 0; height: 30%; z-index: 6; pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(255, 90, 90, 0.13), transparent);
  animation: dbzNbScan 650ms ease-out 1 forwards;
}
@keyframes dbzNbScan { from { transform: translateY(-100%); } to { transform: translateY(360%); } }

/* ============================================================================
   COCKPIT MOBILE (v3 phone redesign) — ONE FULL-SCREEN VIEW + a BOTTOM TAB BAR.
   ----------------------------------------------------------------------------
   The old phone layout stacked everything at once (top bar + tab strip + a 42dvh
   pane + the terminal + hud + macros + input + a floating d-pad) so the game
   output was a tiny sliver and the d-pad covered content. The redesign collapses
   the cockpit to a single-view model:
     - slim top bar: lamp + status + ONE [gear] menu (the utility buttons fold
       into the gear's overflow menu),
     - MAIN AREA = exactly one of GAME(terminal) / MAP / STATS / NEAR / CHAT,
       filling the whole space between the bar and the input,
     - a big-tap BOTTOM TAB BAR just above the pinned input (thumb zone),
     - NO d-pad.
   Desktop (>900px) keeps the side-rail cockpit untouched. Required phone/tablet
   sizes: 390x844 (phone) + 768x1024 (tablet portrait) both collapse here.
   ========================================================================= */
.pr-strip { display: none; }              /* the bottom tab bar — mobile only (shown in the media query) */
.play-dpad { display: none !important; }  /* d-pad DROPPED (#388): never rendered on any size / pointer */
.play-omenu { display: none; }            /* the gear overflow menu — mobile only */

@media (max-width: 900px) {
  /* --- single-view grid: bar / main (terminal OR one pane) / macros / tabs / input --- */
  .play.cockpit {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto auto auto;
    grid-template-areas: "bar" "main" "macros" "tabs" "in";
  }
  .play.cockpit > .play-bar    { grid-area: bar; }
  .play.cockpit > .play-out    { grid-area: main; min-width: 0; }
  .play.cockpit > .play-rail   { grid-area: main; }
  .play.cockpit > .play-macros { grid-area: macros; }
  .play.cockpit > .pr-strip    { grid-area: tabs; }
  .play.cockpit > .play-in     { grid-area: in; }

  /* VIEW SWAP (data-view on the panel): GAME shows the terminal + hides the rail; any pane view hides the
     terminal + shows the rail (which shows its one active pane via data-sheet). Only one ever renders. */
  .play.cockpit[data-view='game'] > .play-rail { display: none; }
  .play.cockpit:not([data-view='game']) > .play-out { display: none; }
  .play.cockpit:not([data-view='game']) > .play-rail {
    display: flex; flex-direction: column; min-height: 0; overflow: hidden;
    border: none; padding: 0; gap: 0; background: var(--bg-pane, var(--bg-win));
  }

  /* --- slim top bar: lamp + status + [gear]; the other buttons fold into the gear overflow menu --- */
  .play-bar { min-height: 44px; padding: 0 4.6rem 0 8px; }  /* keep the top-right PING chip clear on mobile too */
  .play-bar .x.mode, .play-bar .x.log, .play-bar .x.alias, .play-bar .x.fs, .play-bar .x.disc { display: none; }
  .play-bar .x.gear { min-height: 40px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; font-size: 1.05em; }
  /* the lone bare span in the bar is the status text — let it ellipsize so the gear never gets pushed off-screen */
  .play-bar > span:not(.lamp):not(.grow):not(.x) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  /* the DISPLAY > "slim bar" toggle must not collapse the (now essential) phone bar — keep it full height */
  .play.cockpit.bar-slim > .play-bar { max-height: none; opacity: 1; padding: 0 8px; }

  /* --- gear OVERFLOW MENU (mobile only): big-tap rows for settings + every folded-away utility --- */
  .play-omenu {
    display: flex; flex-direction: column; position: absolute; z-index: 30;
    right: calc(4px + env(safe-area-inset-right, 0px)); left: auto;
    width: min(20rem, 82vw);
    background: var(--bg-win); border: 1px solid var(--line-lock);
    box-shadow: 0 0.8em 2em rgba(0, 0, 0, 0.6);
  }
  .play-omenu[hidden] { display: none; }
  .play-omenu .pom-it {
    display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
    min-height: 48px; padding: 0.5rem 0.85rem; cursor: pointer; text-align: left;
    background: transparent; border: none; border-bottom: 1px solid var(--line);
  }
  .play-omenu .pom-it:last-child { border-bottom: none; }
  .play-omenu .pom-it:active { background: var(--bg-raise); }
  .play-omenu .pom-l { font-family: var(--mono); font-size: 0.98em; letter-spacing: 0.04em; color: var(--term-accent, var(--green)); }
  .play-omenu .pom-s { font-family: var(--ui); font-size: 0.72em; letter-spacing: 0.03em; color: var(--fg-faint); }
  .play-omenu .pom-s:empty { display: none; }

  /* --- MAIN-area panes fill full width/height (no 42dvh sliver, no inner height caps) --- */
  .play.cockpit .pr-card { display: none !important; }
  .play.cockpit .play-rail[data-sheet='map'] .pr-map:not(.off),
  .play.cockpit .play-rail[data-sheet='stats'] .pr-stats:not(.off),
  .play.cockpit .play-rail[data-sheet='nearby'] .pr-nearby:not(.off),
  .play.cockpit .play-rail[data-sheet='chat'] .pr-chat:not(.off) {
    display: flex !important; flex: 1 1 auto; min-height: 0;
  }
  /* MAP: the grid wrap grows to the whole pane so fitMapFont scales the glyphs to fill the phone WIDTH */
  .play.cockpit .pr-map { min-height: 0; }
  .play.cockpit .pr-map .pr-gridwrap { flex: 1 1 auto; min-height: 0; }
  .play.cockpit .pr-grid { max-height: none; }
  /* the map's EXIT COMPASS is kept in the MAP view (big tap targets) — the only tap-move control now the
     d-pad is gone, and it isn't a floating overlay: it sits under the full-screen map, not over the game. */
  .play.cockpit .pr-compass { grid-template-columns: repeat(3, 1fr); gap: 4px; padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px)); flex: none; }
  .play.cockpit .pr-compass button { height: 46px; font-size: 0.9em; }
  /* STATS / NEARBY / CHAT: fill + scroll internally (page never scrolls) */
  .play.cockpit .pr-nearby .nb-wrap { flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto; align-content: flex-start; }
  .play.cockpit .pr-chat .pr-chatlog { flex: 1 1 auto; min-height: 0; }
  /* secondary controls inside the now-full-screen panes get comfortable phone tap targets + readable text:
     the chat sub-tabs (all/ooc/say/tell), the nearby target chips, and the chip quick-action menu. */
  .play.cockpit .pr-ctabs { gap: 4px; padding: 5px 6px 0; }
  .play.cockpit .pr-ctab { min-height: 38px; padding: 0 14px; font-size: 0.82em; }
  .play.cockpit .pr-nearby .nb-chip { padding: 9px 12px; font-size: 0.95em; }
  .play.cockpit .qmenu { font-size: 0.9em; min-width: 13rem; }
  .play.cockpit .qmenu .qm-it { min-height: 42px; display: flex; align-items: center; }

  /* --- BOTTOM TAB BAR: 5 equal, big, thumb-zone tabs riding just above the input --- */
  .play.cockpit > .pr-strip {
    display: flex; gap: 0; flex: none; overflow: visible;
    border-top: 1px solid var(--line-hot); background: var(--bg-pane, var(--bg-win));
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .play.cockpit > .pr-strip button {
    flex: 1 1 0; min-width: 0; min-height: 50px; padding: 0 2px; position: relative;
    display: inline-flex; align-items: center; justify-content: center; gap: 0.4ch;
    font-family: var(--mono); font-size: 0.8em; letter-spacing: 0.05em;
    color: var(--fg-dim); background: transparent;
    border: none; border-right: 1px solid var(--line); border-radius: 0;
    cursor: pointer; touch-action: manipulation;
  }
  .play.cockpit > .pr-strip button:last-child { border-right: none; }
  .play.cockpit > .pr-strip button.on {
    color: var(--term-accent, var(--green));
    box-shadow: inset 0 -3px 0 var(--term-accent, var(--green));
    background: color-mix(in srgb, var(--term-accent, #4df2a2) 9%, transparent);
  }
  .play.cockpit > .pr-strip button.off { display: none; }
  .play.cockpit > .pr-strip button.ping::after {
    content: ''; position: absolute; top: 7px; right: 8px; width: 7px; height: 7px; border-radius: 50%;
    background: var(--orange);
  }

  /* >=16px game input under the breakpoint: prevents the iOS focus zoom */
  .play.cockpit .play-in .play-command-input,
  .play.cockpit .play-in .play-command-password { font-size: max(16px, 0.95em); }
  /* keep the input clear of the home indicator / rounded corner (safe-area); fitHeight already pins the
     whole cockpit above the on-screen keyboard via visualViewport. Applies to classic + cockpit alike. */
  .play-in { padding-bottom: calc(0.4rem + env(safe-area-inset-bottom, 0px)); }
  .play-in .play-command-input, .play-in .play-command-password { font-size: max(16px, 0.95em); }
}

/* ---- reduced motion: the second belt — kills every cockpit animation ---------- */
/* (owner decision) the OS "reduce motion" setting is intentionally IGNORED — the cockpit fx animations
   (aura burst, screen shake, electricity, damage flashes) always play. The reduced-motion kill block
   that lived here was removed; use the in-client effects toggles to reduce motion instead. */

/* ---- FULLSCREEN (the [⛶] button; F11-style) ------------------------------------
   When .play is the browser fullscreen element, override the app.css height cap so it
   fills the ENTIRE viewport (not the min(...,calc) desktop size), with no outer border. */
.play.is-fs,
.play:fullscreen,
.play:-webkit-full-screen {
  height: 100vh !important;
  height: 100dvh !important;
  width: 100vw !important;
  max-width: none !important;
  border: 0 !important;
  border-radius: 0 !important;
}
/* The fullscreen element is the play-VIEW (wrap) — so the floating PING chip stays visible. Make the wrap
   fill the screen and its inner .play fill the wrap (the .play.is-fs rule above still sizes .play). */
.play-view:fullscreen,
.play-view:-webkit-full-screen {
  width: 100vw !important; height: 100vh !important; height: 100dvh !important; overflow: hidden; background: var(--bg-well);
}
.play-view:fullscreen > .play,
.play-view:-webkit-full-screen > .play {
  height: 100vh !important; height: 100dvh !important; width: 100vw !important; max-width: none !important; border: 0 !important; border-radius: 0 !important;
}
/* the browser paints the fullscreen backdrop black by default — keep our bg instead. Use the active
   theme's --term-bg (set by .play[data-theme=...]) so themes still apply in fullscreen; fall back to
   --bg-well when no theme is set (the default "scouter" chrome removes data-theme). */
.play:fullscreen { background: var(--term-bg, var(--bg-well)); }
.play:-webkit-full-screen { background: var(--term-bg, var(--bg-well)); }

/* ============================================================================
   FULL CLIENT (opt-in third layout)
   ----------------------------------------------------------------------------
   The map is the world stage and fills the entire play viewport. Existing
   cockpit cards are reused as HUD overlays, so there is one state consumer,
   one map renderer, and no duplicated stream routing. Classic and Cockpit do
   not match .full-client and therefore retain their established layouts.
   ========================================================================= */
.play.full-client {
  --fc-console-h: clamp(9.5rem, 23vh, 16.5rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) var(--fc-console-h) auto auto;
  grid-template-areas: "bar" "stage" "out" "macros" "in";
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background:
    radial-gradient(ellipse at 50% 42%, color-mix(in srgb, var(--term-accent, #4df2a2) 9%, #101a17), #030706 68%),
    var(--term-bg, var(--bg-well));
}

/* The ordinary client chrome remains usable, but reads as an overlaid game HUD
   rather than a row that boxes the world in. */
.play.full-client > .play-bar {
  grid-area: bar; z-index: 20; position: relative;
  min-width: 0; border-bottom-color: rgba(150, 220, 190, .14);
  background: linear-gradient(180deg, rgba(3, 8, 6, .94), rgba(3, 8, 6, .66));
  backdrop-filter: blur(8px);
}
.play.full-client.bar-slim > .play-bar { max-height: none; opacity: 1; padding-top: .3rem; padding-bottom: .3rem; }

/* The rail is no longer a column. It is a transparent, viewport-sized HUD
   plane, and its cards are anchored individually over the map. */
.play.full-client > .play-rail {
  display: block; position: absolute; inset: 0; z-index: 2;
  min-height: 0; padding: 0; overflow: hidden; border: 0; background: transparent;
  pointer-events: none; font-size: clamp(14px, 1vw, 17px);
}
.play.full-client > .rail-split,
.play.full-client > .pr-strip,
.play.full-client .pr-resize,
.play.full-client .pr-collapse { display: none !important; }
.play.full-client .play-rail > .pr-card {
  position: absolute; z-index: 5; flex: none !important;
  pointer-events: auto; overflow: hidden;
  border-color: color-mix(in srgb, var(--term-accent, #4df2a2) 36%, transparent);
  background: linear-gradient(145deg, rgba(5, 13, 10, .68), rgba(2, 6, 5, .54));
  box-shadow: 0 .65rem 1.8rem rgba(0, 0, 0, .32), inset 0 1px rgba(255,255,255,.045);
  backdrop-filter: blur(6px) saturate(1.08);
}

/* WORLD STAGE: the card and render box cover the complete client, including
   behind the console. The HUD never changes the render's measured size. */
.play.full-client .play-rail > .pr-map {
  display: flex !important; inset: 0; z-index: 0; width: auto; height: auto;
  max-width: none; max-height: none !important; min-height: 0;
  border: 0; background:
    radial-gradient(ellipse at center, rgba(24, 45, 36, .36), rgba(1, 4, 3, .82) 78%);
  box-shadow: none; backdrop-filter: none; pointer-events: none;
}
.play.full-client .pr-map::before { display: none; }
.play.full-client .pr-map .pr-gridwrap {
  position: absolute; inset: 0; width: auto; height: auto;
  min-height: 0; margin: 0; overflow: hidden;
}
.play.full-client .pr-map .pr-grid {
  position: absolute; top: 0; left: 0; right: auto; bottom: auto;
  width: max-content; transform-origin: top left;
}
.play.full-client .pr-rownums { display: none; }

/* Environment and location are detached from the map's flex flow and become a
   compact top-right readout. */
.play.full-client .pr-map > .pr-status {
  position: absolute; z-index: 8; top: 3.65rem; right: .75rem;
  width: min(47vw, 44rem); min-height: 0;
  border: 1px solid color-mix(in srgb, var(--term-accent, #4df2a2) 28%, transparent);
  background: rgba(3, 9, 7, .66); box-shadow: 0 .45rem 1.5rem rgba(0,0,0,.30);
  backdrop-filter: blur(6px); pointer-events: auto;
}
.play.full-client .pr-map > .pr-maploc {
  position: absolute; z-index: 8; top: 6.35rem; right: .75rem;
  width: min(36vw, 32rem); box-sizing: border-box;
  justify-content: flex-end; border: 1px solid rgba(150,220,190,.13);
  background: rgba(3, 9, 7, .62); backdrop-filter: blur(6px); pointer-events: auto;
}
.play.full-client .pr-map > .pr-compass { display: none; }

/* Primary HUD anchors. */
.play.full-client .play-rail > .pr-stats {
  display: flex !important; top: 3.65rem; left: .75rem;
  width: clamp(17rem, 24vw, 25rem); height: auto !important;
  min-height: 6rem !important; max-height: none !important;
}
.play.full-client .play-rail > .pr-stats.off,
.play.full-client .play-rail > .pr-target.off { display: none !important; }
.play.full-client .play-rail > .pr-target {
  top: 3.65rem; left: 50%; transform: translateX(-50%);
  width: clamp(18rem, 29vw, 31rem); height: auto !important; max-height: none !important;
}
.play.full-client .play-rail > .pr-nearby {
  top: 10rem; right: .75rem; width: clamp(14rem, 19vw, 20rem);
  height: clamp(9rem, 28vh, 19rem) !important; max-height: 19rem !important;
}
.play.full-client .pr-nearby .nb-wrap { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.play.full-client .play-rail > .pr-chat {
  left: .75rem; bottom: calc(var(--fc-console-h) + 7rem);
  width: clamp(20rem, 32vw, 34rem); height: clamp(9rem, 21vh, 15rem) !important;
  max-height: 15rem !important;
}
.play.full-client .pr-chat .pr-chatlog { flex: 1 1 auto; min-height: 0; }
.play.full-client .play-rail > .pr-exits {
  right: .75rem; bottom: calc(var(--fc-console-h) + 7rem);
  width: min(24rem, 30vw); height: auto !important; max-height: none !important;
}
.play.full-client .play-rail > .pr-tips {
  top: 8.2rem; left: .75rem; width: clamp(17rem, 24vw, 25rem);
  height: auto !important; max-height: none !important;
}

/* The main stream is the bottom console requested by the player: directly
   above macros/input, centred and wide, while the world continues behind it. */
.play.full-client > .play-out {
  grid-area: out; z-index: 12; position: relative; min-width: 0; min-height: 0;
  width: 100%; justify-self: stretch;
  box-sizing: border-box; overflow-y: auto;
  border: 1px solid rgba(150, 220, 190, .16); border-bottom: 0;
  background:
    linear-gradient(180deg, rgba(2, 7, 5, .16), rgba(2, 6, 5, .34)),
    radial-gradient(circle at 50% 100%, color-mix(in srgb, var(--term-accent, #4df2a2) 8%, transparent), transparent 56%);
  box-shadow: 0 -.75rem 2rem rgba(0,0,0,.09), inset 0 1px rgba(255,255,255,.035);
  /* Keep the world visible, but make map glyphs recede underneath the text.
     This affects only the backdrop; output text remains fully opaque. */
  backdrop-filter: brightness(.48) saturate(.62) blur(1px);
  -webkit-backdrop-filter: brightness(.48) saturate(.62) blur(1px);
  text-shadow:
    0 1px 1px rgba(0,0,0,.98),
    0 0 3px rgba(0,0,0,.92),
    0 0 6px rgba(0,0,0,.58);
  padding-top: .45rem;
}
.play.full-client > .play-macros {
  grid-area: macros; z-index: 13; position: relative;
  width: 100%; justify-self: stretch;
  background: rgba(3, 8, 6, .97); backdrop-filter: blur(9px);
}
.play.full-client > .play-in {
  grid-area: in; z-index: 14; position: relative;
  width: 100%; justify-self: stretch;
  box-sizing: border-box; border: 1px solid rgba(150,220,190,.18);
  background: linear-gradient(180deg, rgba(10, 22, 17, .985), rgba(3, 8, 6, .995));
  backdrop-filter: blur(10px);
}
.play.full-client > .play-pill { z-index: 18; }

/* Full Client edit affordances. Headers move panes, lower-right grips resize
   them, and the thin bar atop the console changes console height. They are
   completely absent from Classic and the established Cockpit. */
.fc-resize, .fc-console-resize { display: none; }
.play.full-client .fc-resize {
  display: block; position: absolute; right: 0; bottom: 0; z-index: 30;
  width: 17px; height: 17px; cursor: nwse-resize; touch-action: none;
  background:
    linear-gradient(135deg, transparent 47%, color-mix(in srgb, var(--term-accent, #4df2a2) 70%, transparent) 49%, transparent 53%),
    linear-gradient(135deg, transparent 64%, color-mix(in srgb, var(--term-accent, #4df2a2) 42%, transparent) 66%, transparent 70%);
  opacity: .42;
}
.play.full-client .pr-card:hover > .fc-resize,
.play.full-client .pr-status:hover > .fc-resize,
.play.full-client .pr-maploc:hover > .fc-resize { opacity: .95; }
.play.full-client .pr-hd,
.play.full-client .pr-ctabs,
.play.full-client .ex-line,
.play.full-client .tip-line,
.play.full-client .pr-status,
.play.full-client .pr-maploc { cursor: move; touch-action: none; }
.play.full-client .pr-hd button,
.play.full-client .pr-ctab,
.play.full-client .ex-chip { cursor: pointer; }
.play.full-client .fc-moving {
  z-index: 40 !important; user-select: none;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--term-accent, #4df2a2) 72%, transparent),
              0 1rem 2.8rem rgba(0,0,0,.62) !important;
}
.play.full-client > .fc-console-resize {
  display: block; grid-area: out; align-self: start; justify-self: center;
  position: relative; z-index: 17; width: 100%;
  height: 12px; margin-top: -5px; cursor: ns-resize; touch-action: none;
}
.play.full-client > .fc-console-resize::after {
  content: ''; position: absolute; left: 50%; top: 4px; width: 4.5rem; height: 3px;
  transform: translateX(-50%); border-radius: 4px;
  background: color-mix(in srgb, var(--term-accent, #4df2a2) 45%, rgba(255,255,255,.18));
  box-shadow: 0 0 8px color-mix(in srgb, var(--term-accent, #4df2a2) 18%, transparent);
  opacity: .58;
}
.play.full-client > .fc-console-resize:hover::after { opacity: 1; }

/* Short displays preserve the world by compacting secondary HUD surfaces. */
@media (max-height: 760px) and (min-width: 901px) {
  .play.full-client { --fc-console-h: clamp(7.5rem, 20vh, 10.5rem); }
  .play.full-client .play-rail > .pr-chat { height: 8rem !important; bottom: calc(var(--fc-console-h) + 6.6rem); }
  .play.full-client .play-rail > .pr-nearby { height: 9rem !important; }
  .play.full-client .play-rail > .pr-tips { display: none !important; }
}

/* Phone/tablet Full Client remains map-first. The current mobile Cockpit is
   untouched; these overrides apply only when Full Client was explicitly chosen. */
@media (max-width: 900px) {
  .play.full-client {
    --fc-console-h: clamp(7.5rem, 21vh, 11rem);
    grid-template-rows: auto minmax(0, 1fr) var(--fc-console-h) auto auto;
  }
  .play.full-client > .play-rail { display: block; position: absolute; inset: 0; padding: 0; background: transparent; }
  .play.full-client[data-view='game'] > .play-rail { display: block; }
  .play.full-client:not([data-view='game']) > .play-out { display: block; }
  .play.full-client .play-rail > .pr-card { display: none !important; }
  .play.full-client .play-rail > .pr-map,
  .play.full-client .play-rail > .pr-stats,
  .play.full-client .play-rail > .pr-target { display: flex !important; }
  .play.full-client .play-rail > .pr-stats { top: 3.25rem; left: .35rem; width: min(15rem, 58vw); }
  .play.full-client .play-rail > .pr-target { top: 7.2rem; left: .35rem; transform: none; width: min(17rem, 66vw); }
  .play.full-client .pr-map > .pr-status { top: 3.25rem; right: .35rem; width: min(12rem, 40vw); }
  .play.full-client .pr-map > .pr-maploc { top: 7rem; right: .35rem; width: min(11rem, 38vw); }
  .play.full-client > .play-out,
  .play.full-client > .play-macros,
  .play.full-client > .play-in { width: 100%; }
  .play.full-client > .play-out { font-size: max(14px, .82em); }
  .play.full-client .fc-resize,
  .play.full-client > .fc-console-resize { display: none !important; }
}

/* ============================================================================
   PRE-GAME OVERLAY (js/views/play-login.js) — graphical login / register /
   character-select shown BEFORE the terminal. wrap.dataset.plg drives the
   layout: pregame (overlay covers the terminal — login, character-select, and all
   graphical account actions) / playing (overlay gone, play.js owns it).
   ========================================================================= */
.play-view { position: relative; }

/* The cockpit is fully constructed before pregame attaches, but it must not remain a second painted
   application behind the opaque gate. `visibility` preserves its measured box for the absolute overlay
   while suspending descendant paint/compositing; it is restored automatically on world entry. */
.play-view[data-plg='pregame'] > .play { visibility: hidden; pointer-events: none; }
.play-view[data-plg='playing'] > .play { visibility: visible; }

/* PREGAME: the overlay is an opaque cover over the (already laid-out) terminal. */
.play-view[data-plg='pregame'] .plg-overlay {
  position: absolute; inset: 0; z-index: 12;
  overflow: auto; -webkit-overflow-scrolling: touch;
  background:
    radial-gradient(120% 90% at 50% -10%, rgba(77,242,162,0.08), transparent 60%),
    radial-gradient(90% 70% at 110% 120%, rgba(255,194,75,0.06), transparent 55%),
    var(--bg-well);
}
/* PLAYING: gone entirely — the terminal is the whole surface. */
.play-view[data-plg='playing'] .plg-overlay { display: none; }

/* subtle CRT scanline wash over the pregame overlay (kills under reduced-motion) */
.play-view[data-plg='pregame'] .plg-overlay::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 0; opacity: 0.5;
  background: repeating-linear-gradient(to bottom, rgba(0,0,0,0) 0 2px, rgba(0,0,0,0.14) 2px 3px);
}

/* Pregame is an interface, not an idle GPU benchmark. Keep the atmospheric composition but make its
   large layers static; entering/spinner animations remain available for immediate progress feedback. */
.play-view[data-plg='pregame'] .plg-stars i,
.play-view[data-plg='pregame'] .plg-nebula,
.play-view[data-plg='pregame'] .plg-star,
.play-view[data-plg='pregame'] .plg-card::after { animation: none !important; }
.play-view[data-plg='pregame'] .plg-card::after { display: none; }
.play-view[data-plg='pregame'] .plg-card { backdrop-filter: none; }

/* ---- stage / brand -------------------------------------------------------- */
.plg-stage {
  position: relative; z-index: 1; min-height: 100%;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(0.8rem, 3vh, 2.2rem) 1rem;
}
.plg-stage.plg-selstage { align-items: flex-start; }
.plg-stage-in { width: 100%; max-width: 30rem; }
.plg-stage-in.wide { max-width: 62rem; }
.plg-brand { text-align: center; margin-bottom: 1.1rem; }
.plg-brand.small { margin-bottom: 0.7rem; }
.plg-logo {
  font-family: var(--mono); font-weight: 400; letter-spacing: 0.14em;
  font-size: clamp(1.5rem, 4.6vw, 2.5rem); line-height: 1; color: var(--fg);
  text-shadow: 0 0 0.5em rgba(77,242,162,0.25);
}
.plg-brand.small .plg-logo { font-size: clamp(1.15rem, 3vw, 1.6rem); }
.plg-star { color: var(--gold); }
.plg-sub {
  margin-top: 0.4rem; color: var(--amber); letter-spacing: 0.42em; text-indent: 0.42em;
  font-size: clamp(0.62rem, 1.5vw, 0.8rem);
}
.plg-brand.small .plg-sub { letter-spacing: 0.3em; text-indent: 0.3em; font-size: 0.62rem; }

/* ---- card frame ----------------------------------------------------------- */
.plg-card {
  border: 1px solid var(--line-hot); border-radius: 4px;
  background: linear-gradient(180deg, rgba(11,16,13,0.96), rgba(6,9,8,0.96));
  box-shadow: 0 0 0 1px rgba(0,0,0,0.4), 0 1.4rem 3rem rgba(0,0,0,0.5), inset 0 0 2.5rem rgba(0,0,0,0.4);
  padding: 1.1rem 1.15rem 1.2rem;
}
.plg-card-hd {
  font-family: var(--mono); color: var(--green); letter-spacing: 0.24em;
  font-size: 0.74rem; text-transform: uppercase; margin-bottom: 0.9rem;
  padding-bottom: 0.55rem; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 0.6ch;
}
.plg-card-hd::before { content: '>'; color: var(--green-dim); }

/* ---- boot / spinner ------------------------------------------------------- */
.plg-boot { display: flex; align-items: center; gap: 0.8ch; color: var(--fg-dim); justify-content: center; padding: 1rem 0; }
.plg-spin {
  width: 1.05em; height: 1.05em; border-radius: 50%; flex: none;
  border: 2px solid var(--line-lock); border-top-color: var(--green);
  animation: plgSpin 0.7s linear infinite;
}
@keyframes plgSpin { to { transform: rotate(360deg); } }
.plg-entername { font-size: 1.15rem; color: var(--gold); }

/* ---- auth tabs + forms ---------------------------------------------------- */
.plg-tabs { display: flex; gap: 0.4rem; margin-bottom: 1rem; }
.plg-tab {
  flex: 1; padding: 0.55rem 0.4rem; font-family: var(--mono); font-size: 0.76rem; letter-spacing: 0.16em;
  color: var(--fg-faint); background: var(--bg-well); border: 1px solid var(--line);
  border-radius: 3px; cursor: pointer; text-transform: uppercase; transition: color 120ms, border-color 120ms;
}
.plg-tab:hover { color: var(--fg-dim); }
.plg-tab.on { color: #03170d; background: var(--green); border-color: var(--green); font-weight: 700; }

.plg-form { display: flex; flex-direction: column; gap: 0.7rem; }
.plg-field { display: flex; flex-direction: column; gap: 0.28rem; }
.plg-lab { font-size: 0.66rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--fg-faint); }
.plg-inp {
  font-family: var(--mono); font-size: 0.95rem; color: var(--fg);
  background: var(--bg-well); border: 1px solid var(--line-hot); border-radius: 3px;
  padding: 0.55rem 0.7rem; outline: none; caret-color: var(--green); width: 100%; box-sizing: border-box;
}
.plg-inp:focus { border-color: var(--green); box-shadow: 0 0 0 1px rgba(77,242,162,0.35), 0 0 0.7em rgba(77,242,162,0.2); }
.plg-note { color: var(--fg-dim); font-size: 0.82rem; margin: 0 0 0.2rem; }
.plg-note.center { text-align: center; }
.plg-foot { color: var(--fg-faint); font-size: 0.74rem; text-align: center; margin: 0.9rem 0 0; }
.plg-msg { min-height: 1.15em; font-size: 0.82rem; color: var(--fg-dim); }
.plg-msg.err { color: var(--red); }
.plg-msg.ok { color: var(--green); }
.plg-actions { display: flex; gap: 0.5rem; margin-top: 0.35rem; }

/* ---- buttons -------------------------------------------------------------- */
.plg-btn {
  font-family: var(--mono); font-size: 0.78rem; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.6rem 0.95rem; border-radius: 3px; cursor: pointer; white-space: nowrap;
  color: var(--green); background: transparent; border: 1px solid var(--line-lock);
  transition: color 120ms, background 120ms, border-color 120ms; flex: 1;
}
.plg-btn:hover { color: #03170d; background: var(--green); border-color: var(--green); }
.plg-btn.primary { color: #03170d; background: var(--green); border-color: var(--green); font-weight: 700; }
.plg-btn.primary:hover { box-shadow: 0 0 0.8em rgba(77,242,162,0.4); filter: brightness(1.06); }
.plg-btn.ghost { color: var(--fg-dim); border-color: var(--line-hot); flex: 0 1 auto; }
.plg-btn.ghost:hover { color: var(--fg); background: var(--bg-raise); border-color: var(--line-lock); }
.plg-btn.danger { color: var(--red); border-color: rgba(255,77,77,0.35); }
.plg-btn.danger:hover { color: #1a0303; background: var(--red); border-color: var(--red); }
.plg-btn.block { width: 100%; flex: none; margin-top: 0.8rem; }
.plg-btn:disabled { opacity: 0.5; cursor: default; pointer-events: none; }

/* ---- character select ----------------------------------------------------- */
.plg-selhead { display: flex; align-items: baseline; justify-content: space-between; gap: 1ch; margin-bottom: 0.9rem; }
.plg-sel-t { font-family: var(--mono); letter-spacing: 0.18em; color: var(--fg); font-size: 1rem; }
.plg-sel-s { color: var(--fg-faint); font-size: 0.74rem; margin-top: 0.2rem; }
.plg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 0.8rem; }
.plg-empty { color: var(--fg-dim); padding: 1.4rem; text-align: center; border: 1px dashed var(--line-hot); border-radius: 4px; }

.plg-charcard {
  border: 1px solid var(--line-hot); border-radius: 4px; padding: 0.85rem 0.9rem 0.95rem;
  background: linear-gradient(180deg, rgba(15,21,18,0.9), rgba(6,9,8,0.9));
  cursor: pointer; outline: none; transition: border-color 130ms, box-shadow 130ms, transform 130ms;
}
.plg-charcard:hover, .plg-charcard:focus-visible {
  border-color: var(--green); transform: translateY(-2px);
  box-shadow: 0 0 0 1px rgba(77,242,162,0.25), 0 0.7rem 1.4rem rgba(0,0,0,0.45);
}
.plg-chead { display: flex; align-items: center; gap: 0.7ch; margin-bottom: 0.7rem; }
.plg-emblem {
  width: 2.4rem; height: 2.4rem; flex: none; display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 1.25rem; font-weight: 700;
  border: 1px solid var(--line-lock); border-radius: 3px; background: var(--bg-well);
  box-shadow: inset 0 0 0.8rem rgba(0,0,0,0.5); text-shadow: 0 0 0.5em currentColor;
}
.plg-cnamewrap { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.plg-cname { font-size: 1.05rem; line-height: 1.1; word-break: break-word; }
.plg-badge {
  align-self: flex-start; font-size: 0.6rem; letter-spacing: 0.1em; padding: 0.05rem 0.4rem;
  border-radius: 2px; border: 1px solid var(--line-lock); color: var(--fg-dim);
}
.plg-badge.wiz { color: var(--amber); border-color: rgba(255,180,84,0.4); }
.plg-badge.owner { color: #111; background: var(--gold); border-color: var(--gold); font-weight: 700; }
.plg-crows { display: flex; flex-direction: column; gap: 0.15rem; }
.plg-crow { display: flex; justify-content: space-between; gap: 1ch; font-size: 0.84rem; color: var(--fg-dim); align-items: baseline; }
.plg-crow b, .plg-crow span:last-child { color: var(--fg); font-weight: 400; text-align: right; }
.plg-ck { color: var(--fg-faint); letter-spacing: 0.12em; font-size: 0.66rem; }
.plg-pl { color: var(--green) !important; }
.plg-dim { color: var(--fg-faint) !important; }
.plg-align.a-good { color: var(--green) !important; }
.plg-align.a-evil { color: var(--red) !important; }
.plg-align.a-neutral { color: var(--amber) !important; }

.plg-ctl-hd { margin: 1.2rem 0 0.55rem; color: var(--fg-faint); letter-spacing: 0.24em; font-size: 0.66rem; text-transform: uppercase; border-top: 1px solid var(--line); padding-top: 0.8rem; }
.plg-controls { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.plg-controls .plg-btn { flex: 0 1 auto; }

/* ---- account actions: delete / import / change-email (graphical, REST) ---- */
.plg-note.danger { color: var(--red); }
.plg-strong { color: var(--fg); font-weight: 700; }
.plg-confirm { margin: 0 0 0.6rem; }
.plg-confirm p { margin: 0 0 0.35rem; color: var(--fg-dim); font-size: 0.86rem; }

.plg-picklist { display: flex; flex-direction: column; gap: 0.4rem; margin: 0.2rem 0 0.6rem; }
.plg-pickrow {
  display: flex; align-items: center; gap: 1ch; width: 100%; text-align: left; cursor: pointer;
  font-family: var(--mono); font-size: 0.9rem; color: var(--fg);
  background: var(--bg-well); border: 1px solid var(--line-hot); border-radius: 3px; padding: 0.6rem 0.75rem;
  transition: border-color 120ms, background 120ms;
}
.plg-pickrow:hover { border-color: var(--red); background: var(--bg-raise); }
.plg-pickname { font-size: 0.98rem; }
.plg-pickrace { color: var(--fg-dim); font-size: 0.8rem; }
.plg-pickgo { margin-left: auto; color: var(--red); font-size: 0.7rem; letter-spacing: 0.1em; }

/* ---- character creation (graphical forge) --------------------------------- */
.plg-cc-card { max-width: 62rem; }
.plg-cc-title { text-align: center; margin: 0 0 1.1rem; }
.plg-cc-kicker {
  display: inline-block; color: var(--amber); letter-spacing: 0.42em; font-size: 0.62rem;
  text-transform: uppercase; padding: 0 0 0.35rem;
}
.plg-cc-h {
  margin: 0; font-family: var(--mono); font-weight: 700; letter-spacing: 0.14em; font-size: 1.5rem;
  color: var(--fg); text-shadow: 0 0 0.9em rgba(255,140,26,0.35);
}
.plg-cc-h::before, .plg-cc-h::after { content: '\2b25'; color: var(--gold); font-size: 0.6em; vertical-align: middle; margin: 0 0.7ch; opacity: 0.8; }

/* two-column: steps + sticky scouter readout */
.plg-cc-layout { display: grid; grid-template-columns: minmax(0,1fr) 17rem; gap: 1.3rem; align-items: start; }
.plg-cc-steps { min-width: 0; display: flex; flex-direction: column; gap: 1.35rem; }

.plg-cc-step { border-top: 1px solid var(--line); padding-top: 1rem; }
.plg-cc-step:first-child { border-top: none; padding-top: 0; }
.plg-step-hd { display: flex; align-items: center; gap: 0.8ch; margin: 0 0 0.9rem; }
.plg-step-n {
  flex: none; width: 1.85rem; height: 1.85rem; display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-weight: 700; font-size: 0.82rem; color: var(--gold);
  border: 1px solid var(--gold); border-radius: 50%; background: rgba(255,194,75,0.08);
  box-shadow: 0 0 0.7em rgba(255,194,75,0.25), inset 0 0 0.5em rgba(255,194,75,0.12);
}
.plg-step-t { font-family: var(--mono); letter-spacing: 0.16em; font-size: 0.98rem; color: var(--fg); }
.plg-step-s { color: var(--fg-faint); font-size: 0.74rem; margin-top: 0.12rem; }

.plg-cc-blk { margin: 0.85rem 0 0; }
.plg-cc-blk:first-child { margin-top: 0; }
.plg-cc-lab { color: var(--fg-faint); letter-spacing: 0.2em; font-size: 0.63rem; text-transform: uppercase; margin: 0 0 0.45rem; }
.plg-cc-fixed {
  display: inline-block; color: var(--amber); font-size: 0.78rem; letter-spacing: 0.04em;
  background: rgba(255,194,75,0.07); border: 1px dashed rgba(255,194,75,0.35); border-radius: 3px; padding: 0.3rem 0.6rem;
}
.plg-cc-note { color: var(--fg-faint); font-size: 0.8rem; font-style: italic; }
.plg-cc-foot { margin-top: 1.2rem; }

/* option chips */
.plg-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.plg-chip {
  display: inline-flex; align-items: center; gap: 0.55ch;
  font-family: var(--mono); font-size: 0.82rem; cursor: pointer; white-space: nowrap;
  color: var(--fg); background: var(--bg-well); border: 1px solid var(--line-hot); border-radius: 3px;
  padding: 0.42rem 0.8rem; transition: border-color 120ms, background 120ms, box-shadow 120ms, transform 120ms;
}
.plg-chip:hover { border-color: var(--line-lock); background: var(--bg-raise); transform: translateY(-1px); }
.plg-chip.on {
  border-color: var(--green); color: var(--fg); background: rgba(77,242,162,0.12);
  box-shadow: 0 0 0 1px rgba(77,242,162,0.4), inset 0 0 0.7em rgba(77,242,162,0.14);
}
.plg-swatch {
  width: 0.9rem; height: 0.9rem; flex: none; border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.35); box-shadow: 0 0 0.4em rgba(0,0,0,0.6); background: var(--fg-dim);
}
.plg-chip.on .plg-swatch { box-shadow: 0 0 0.5em currentColor; }
.plg-chip-ic { font-size: 0.95rem; line-height: 1; opacity: 0.85; }
.plg-chip.on .plg-chip-ic { opacity: 1; }
.plg-gsym { color: var(--amber); margin-right: 0.4ch; font-size: 0.95em; }

/* race card grid */
.plg-racegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.55rem; }
.plg-racecard {
  --rc: var(--green);
  text-align: left; cursor: pointer; display: flex; flex-direction: column; gap: 0.4rem;
  background: linear-gradient(160deg, rgba(18,24,21,0.9), rgba(7,10,9,0.92));
  border: 1px solid var(--line-hot); border-radius: 4px; padding: 0.6rem 0.65rem 0.7rem;
  transition: border-color 130ms, box-shadow 130ms, transform 130ms;
}
.plg-racecard:hover { transform: translateY(-2px); border-color: var(--rc); }
.plg-racecard.on {
  border-color: var(--rc);
  box-shadow: 0 0 0 1px var(--rc), 0 0 1.1em -0.2em var(--rc), inset 0 0 1.4em -0.6em var(--rc);
}
.plg-race-top { display: flex; align-items: center; gap: 0.6ch; }
.plg-race-nm { font-size: 0.92rem; font-weight: 700; line-height: 1.1; word-break: break-word; }

/* dragon ball emblem (orange sphere + red stars) */
.plg-ball {
  position: relative; flex: none; width: 1.75rem; height: 1.75rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 34% 28%, #ffe9b3 0%, #ffbd4d 32%, #ff9422 60%, #e2680f 100%);
  box-shadow:
    inset -0.14em -0.2em 0.42em rgba(120,48,0,0.6),
    inset 0.1em 0.12em 0.32em rgba(255,255,255,0.55),
    0 0 0.55em rgba(255,140,26,0.5);
}
.plg-ball.big { width: 2.7rem; height: 2.7rem; }
.plg-ball-stars {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  width: 74%; gap: 0.02em 0.04em; line-height: 1;
}
.plg-ball-stars.s2, .plg-ball-stars.s3 { width: 64%; }
.plg-ball-stars.s1 { width: auto; }
.plg-star-r {
  color: #d81e2c; font-size: 0.46rem; line-height: 1;
  text-shadow: 0 0 0.08em rgba(90,0,0,0.6);
}
.plg-ball.big .plg-star-r { font-size: 0.62rem; }
.plg-race-blurb { color: var(--fg-dim); font-size: 0.71rem; line-height: 1.3; min-height: 1.85em; }
.plg-race-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.plg-race-tag {
  font-size: 0.56rem; letter-spacing: 0.08em; text-transform: uppercase; padding: 0.08rem 0.4rem;
  border-radius: 2px; border: 1px solid var(--line-lock); color: var(--fg-faint);
}
.plg-race-tag.good { color: var(--green); border-color: rgba(77,242,162,0.4); }
.plg-race-tag.evil { color: var(--red); border-color: rgba(255,90,90,0.4); }
.plg-race-tag.neu { color: var(--amber); border-color: rgba(255,194,75,0.4); }

/* live scouter readout */
.plg-scouter {
  position: sticky; top: 0.5rem; align-self: start;
  border: 1px solid var(--line-lock); border-radius: 5px; overflow: hidden;
  background:
    linear-gradient(180deg, rgba(70,245,184,0.05), transparent 40%),
    linear-gradient(180deg, rgba(10,14,12,0.96), rgba(6,9,8,0.96));
  box-shadow: inset 0 0 2.2em -0.6em rgba(77,242,162,0.25);
}
.plg-pv-hd {
  display: flex; align-items: center; gap: 0.7ch; padding: 0.85rem 0.85rem 0.75rem;
  border-bottom: 1px solid var(--line-hot); background: rgba(77,242,162,0.04);
}
.plg-pv-emblem { flex: none; display: flex; align-items: center; justify-content: center; }
.plg-pv-id { min-width: 0; }
.plg-pv-name { font-family: var(--mono); font-weight: 700; font-size: 1.02rem; letter-spacing: 0.04em; line-height: 1.1; word-break: break-word; }
.plg-pv-power {
  color: var(--green); font-size: 0.68rem; letter-spacing: 0.14em; margin-top: 0.2rem;
  text-shadow: 0 0 0.5em rgba(77,242,162,0.5);
}
.plg-pv-power::before { content: '\25c8\00a0'; opacity: 0.8; }
.plg-pv-rows { padding: 0.55rem 0.85rem 0.8rem; display: flex; flex-direction: column; gap: 0.02rem; }
.plg-pv-row { display: flex; gap: 1ch; justify-content: space-between; align-items: baseline; padding: 0.28rem 0; border-bottom: 1px dotted rgba(255,255,255,0.06); font-size: 0.8rem; }
.plg-pv-row:last-child { border-bottom: none; }
.plg-pv-k { color: var(--fg-faint); letter-spacing: 0.12em; font-size: 0.6rem; flex: none; }
.plg-pv-v { color: var(--fg); text-align: right; word-break: break-word; }

.plg-toggle {
  display: flex; align-items: flex-start; gap: 0.7ch; cursor: pointer; color: var(--fg-dim); font-size: 0.84rem;
  background: rgba(255,194,75,0.05); border: 1px solid rgba(255,194,75,0.22); border-radius: 4px; padding: 0.7rem 0.8rem;
}
.plg-toggle:hover { border-color: rgba(255,194,75,0.5); }
.plg-toggle b { color: var(--amber); font-weight: 700; }
.plg-check { width: 1rem; height: 1rem; margin-top: 0.15rem; flex: none; accent-color: var(--amber); cursor: pointer; }

@media (max-width: 640px) {
  .plg-cc-layout { grid-template-columns: 1fr; }
  .plg-scouter { position: static; order: -1; }
}

/* ---- always-on ping chip -------------------------------------------------- */
.plg-ping {
  position: absolute; top: 0.45rem; right: 0.55rem; z-index: 20; pointer-events: none;
  font-family: var(--mono); font-size: 0.66rem; font-weight: 700; letter-spacing: 0.08em;
  padding: 0.14rem 0.5rem; border-radius: 3px; color: var(--green);
  background: rgba(4,6,5,0.82); border: 1px solid rgba(77,242,162,0.3);
  text-shadow: 0 0 0.5em rgba(77,242,162,0.55);
}
.plg-ping::before { content: '\25c8\00a0'; color: currentColor; }
.plg-ping.good { color: #6cffcb; border-color: rgba(77,242,162,0.45); }
.plg-ping.ok { color: var(--amber); border-color: rgba(255,194,75,0.4); text-shadow: 0 0 0.5em rgba(255,194,75,0.5); }
.plg-ping.bad { color: #ff7a7a; border-color: rgba(255,90,90,0.4); text-shadow: 0 0 0.5em rgba(255,90,90,0.5); }
/* in the playing phase keep it only SLIGHTLY dimmed so it stays legible over the cockpit chrome */
.play-view[data-plg='playing'] .plg-ping { opacity: 0.9; }
.play-view[data-plg='playing'] .plg-ping:hover { opacity: 1; }

/* ---- toast ---------------------------------------------------------------- */
.plg-toast {
  position: absolute; left: 50%; bottom: 1.2rem; transform: translate(-50%, 1rem); z-index: 30;
  background: var(--bg-raise); border: 1px solid var(--line-lock); border-radius: 3px;
  color: var(--fg); font-size: 0.82rem; padding: 0.5rem 0.9rem; max-width: 90%;
  opacity: 0; pointer-events: none; transition: opacity 180ms, transform 180ms;
}
.plg-toast.on { opacity: 1; transform: translate(-50%, 0); }

/* ---- mobile --------------------------------------------------------------- */
@media (max-width: 640px) {
  .plg-stage { padding: 0.8rem 0.6rem; }
  .plg-grid { grid-template-columns: 1fr; }
  .plg-controls .plg-btn { flex: 1 1 45%; }
  .plg-selhead { flex-direction: column; }
}
/* (owner decision) reduced-motion is intentionally ignored here too — the pregame spinner / overlay /
   card hover animations always play. */

/* ============================================================================
   SNOOP-SPECTATE POPUP (web) — a live read-only mirror of another player's
   cockpit (their map/nearby/PL/form). Driven by `t:"snoop"` state frames.
   ========================================================================= */
.snoop-pop {
  position: fixed; top: 3.4rem; right: 0.8rem; z-index: 60; width: min(23rem, 92vw);
  min-width: 260px; min-height: 200px;
  max-height: min(88vh, 720px);
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, rgba(10,14,12,0.98), rgba(6,9,8,0.98));
  border: 1px solid var(--green); border-radius: 6px;
  box-shadow: 0 0 0 1px rgba(77,242,162,0.25), 0 1.2rem 2.6rem rgba(0,0,0,0.6),
              inset 0 0 2.4em -0.8em rgba(77,242,162,0.3);
  font-family: var(--mono); color: var(--fg); overflow: hidden;
  box-sizing: border-box;
}
.snoop-head {
  display: flex; align-items: center; gap: 0.6ch; padding: 0.45rem 0.6rem;
  background: rgba(77,242,162,0.06); border-bottom: 1px solid var(--line-hot);
  font-size: 0.74rem; letter-spacing: 0.08em; touch-action: none;
  flex: none; cursor: grab; user-select: none;
}
.snoop-dot {
  width: 0.55rem; height: 0.55rem; flex: none; border-radius: 50%; background: var(--red);
  box-shadow: 0 0 0.5em var(--red); animation: snoopPulse 1.4s ease-in-out infinite;
}
@keyframes snoopPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
.snoop-title { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.snoop-head .grow { flex: 1; }
.snoop-x { cursor: pointer; color: var(--fg-dim); font-size: 1.15rem; line-height: 1; padding: 0 0.25rem; }
.snoop-x:hover { color: var(--red); }
.snoop-grid {
  position: relative; padding: 0.5rem 0.6rem; overflow: auto;
  flex: 1 1 auto; min-height: 0;
  font-family: var(--term-font, var(--mono)); font-size: 0.85rem; line-height: 1.12; white-space: pre;
  background: radial-gradient(120% 90% at 50% 0%, rgba(77,242,162,0.05), transparent 60%), var(--bg-well);
  overscroll-behavior: contain;
}
/* the map block is transform-scaled by fitSnoopMap to fill the width — inline-block so its scrollWidth
   measures the true glyph-grid width. */
.snoop-map { display: inline-block; white-space: pre; }
/* (snoop canvas) the glyph-canvas back end fills the grid box; the canvas itself is position:absolute
   (see .pr-canvas) so this wrap gives it a measurable, grid-sized frame. */
.snoop-canvas-wrap { position: absolute; inset: 0; }
.snoop-empty { color: var(--fg-faint); font-style: italic; font-size: 0.8rem; padding: 1.2rem 0.6rem; text-align: center; }
.snoop-stat {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.7rem;
  padding: 0.5rem 0.6rem; border-top: 1px solid var(--line);
  flex: none;
}
.snoop-bar { display: flex; align-items: center; gap: 0.55ch; flex: 1 1 100%; }
.snoop-bar > i { color: var(--fg-faint); font-size: 0.56rem; letter-spacing: 0.14em; font-style: normal; flex: none; }
.snoop-track {
  flex: 1; height: 0.5rem; background: var(--bg-well); border: 1px solid var(--line-hot);
  border-radius: 3px; overflow: hidden;
}
.snoop-fill { display: block; height: 100%; background: var(--green); box-shadow: 0 0 0.5em rgba(77,242,162,0.5); transition: width 180ms; }
.snoop-fill.warn { background: var(--amber); }
.snoop-fill.crit { background: var(--red); }
.snoop-fill.over { background: #6fe9f0; }
.snoop-bar > b { font-size: 0.82rem; }
.snoop-bar .plm { flex: none; }
.snoop-chip { display: inline-flex; align-items: baseline; gap: 0.5ch; font-size: 0.72rem; }
.snoop-chip i { color: var(--fg-faint); font-size: 0.55rem; letter-spacing: 0.1em; font-style: normal; }
.snoop-chip b { color: var(--fg); font-weight: 400; }
.snoop-foot {
  display: flex; flex-direction: column; gap: 0.32rem; padding: 0.45rem 0.6rem 0.6rem;
  border-top: 1px solid var(--line); font-size: 0.72rem;
  flex: none;
}
.snoop-nb, .snoop-exits { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 0.55rem; }
.snoop-nb > i, .snoop-exits > i { color: var(--fg-faint); font-size: 0.55rem; letter-spacing: 0.1em; font-style: normal; flex: none; }
.snoop-mob { font-size: 0.72rem; white-space: nowrap; }
.snoop-mob s { color: var(--fg-faint); text-decoration: none; font-size: 0.62rem; }
.snoop-exits b { color: var(--green); letter-spacing: 0.08em; }
/* impaired mirror: the target can't perceive the world -> dim their map, like their own cockpit does */
.snoop-pop[data-impair="ko"] .snoop-grid,
.snoop-pop[data-impair="blind"] .snoop-grid,
.snoop-pop[data-impair="sleep"] .snoop-grid { opacity: 0.12; }
@media (max-width: 640px) { .snoop-pop { right: 0.4rem; left: 0.4rem; width: auto; } }

/* ============================================================================
   DRAGON RADAR POPUP (web) — a circular scouter radar with a sweeping line and
   orange dragon-ball blips at their real bearings. Driven by `t:"radar"` frames.
   ========================================================================= */
.radar-pop {
  position: fixed; bottom: 1rem; right: 0.8rem; z-index: 61; width: 10.5rem; height: auto;
  background: linear-gradient(180deg, rgba(8,13,10,0.98), rgba(5,8,7,0.98));
  border: 1px solid var(--green); border-radius: 8px;
  box-shadow: 0 0 0 1px rgba(77,242,162,0.25), 0 1.2rem 2.6rem rgba(0,0,0,0.6),
              inset 0 0 2.4em -0.8em rgba(77,242,162,0.35);
  font-family: var(--mono); color: var(--fg); overflow: hidden;
}
.radar-head {
  display: flex; align-items: center; gap: 0.55ch; padding: 0.4rem 0.55rem;
  background: rgba(77,242,162,0.06); border-bottom: 1px solid var(--line-hot);
  font-size: 0.66rem; letter-spacing: 0.1em; touch-action: none;
}
.radar-led {
  width: 0.5rem; height: 0.5rem; flex: none; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0.5em var(--green); animation: radarLed 1.6s ease-in-out infinite;
}
@keyframes radarLed { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }
.radar-title { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.radar-head .grow { flex: 1; }
.radar-x { cursor: pointer; color: var(--fg-dim); font-size: 1.05rem; line-height: 1; padding: 0 0.2rem; }
.radar-x:hover { color: var(--red); }

.radar-scope {
  position: relative; width: 126px; height: 126px; flex: none; margin: 0.6rem auto 0.45rem; border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, rgba(77,242,162,0.14), rgba(77,242,162,0.04) 55%, rgba(3,7,5,0.9) 72%),
    repeating-conic-gradient(from 0deg, rgba(77,242,162,0.05) 0deg 1deg, transparent 1deg 45deg);
  border: 1px solid rgba(77,242,162,0.55);
  box-shadow: inset 0 0 1.6em rgba(0,0,0,0.75), inset 0 0 0.4em rgba(77,242,162,0.35);
  overflow: hidden;
}
.radar-ring { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); border: 1px solid rgba(77,242,162,0.22); border-radius: 50%; }
.radar-ring.r1 { width: 66%; height: 66%; }
.radar-ring.r2 { width: 33%; height: 33%; }
.radar-cross { position: absolute; background: rgba(77,242,162,0.18); }
.radar-cross.v { left: 50%; top: 4%; bottom: 4%; width: 1px; transform: translateX(-0.5px); }
.radar-cross.h { top: 50%; left: 4%; right: 4%; height: 1px; transform: translateY(-0.5px); }
/* the rotating sweep wedge (a fading conic slice) */
.radar-sweep {
  position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  background: conic-gradient(from 0deg, rgba(77,242,162,0.45), rgba(77,242,162,0.14) 26deg, transparent 62deg);
  animation: radarSweep 3.2s linear infinite;
}
@keyframes radarSweep { to { transform: rotate(360deg); } }
/* the expanding "ping" ring, retriggered on each refresh */
.radar-ping { position: absolute; top: 50%; left: 50%; width: 6px; height: 6px; border-radius: 50%; border: 1px solid var(--green); transform: translate(-50%,-50%); opacity: 0; pointer-events: none; }
.radar-ping.go { animation: radarPing 0.85s ease-out 1; }
@keyframes radarPing { 0% { width: 6px; height: 6px; opacity: 0.9; } 100% { width: 120px; height: 120px; opacity: 0; } }
.radar-self { position: absolute; top: 50%; left: 50%; width: 5px; height: 5px; border-radius: 50%; transform: translate(-50%,-50%); background: var(--cyan, #6fe9f0); box-shadow: 0 0 0.5em rgba(111,233,240,0.9); z-index: 2; }
/* the pointer ARROW to the nearest ball — anchored at the scope centre, rotated (by JS) to the ball's dir */
.radar-arrow {
  position: absolute; left: 50%; bottom: 50%; width: 0; height: 44px;
  transform-origin: 50% 100%; transform: translateX(-50%); pointer-events: none; z-index: 3;
}
.radar-arrow::before {   /* shaft */
  content: ''; position: absolute; left: -1.5px; bottom: 2px; width: 3px; height: calc(100% - 12px);
  background: linear-gradient(to top, rgba(255,140,26,0.12), #ff9422); border-radius: 2px;
}
.radar-arrow::after {    /* head */
  content: ''; position: absolute; left: -6px; top: -2px; width: 0; height: 0;
  border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 12px solid #ffbd4d;
  filter: drop-shadow(0 0 3px rgba(255,150,30,0.9));
}
/* a ball on your own tile — a pulsing amber ring at the centre */
.radar-here {
  position: absolute; top: 50%; left: 50%; width: 16px; height: 16px; transform: translate(-50%,-50%);
  border-radius: 50%; border: 2px solid #ffbd4d; box-shadow: 0 0 0.6em rgba(255,150,30,0.9); z-index: 3;
  animation: radarHere 1s ease-in-out infinite;
}
@keyframes radarHere { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }

.radar-status { padding: 0 0.6rem 0.6rem; }
.radar-count { display: block; color: var(--fg-dim); font-size: 0.66rem; letter-spacing: 0.04em; text-align: center; margin-bottom: 0.4rem; }
.radar-cd { display: flex; align-items: center; gap: 0.5ch; font-size: 0.62rem; }
.radar-cd > i { color: var(--fg-faint); letter-spacing: 0.12em; font-style: normal; flex: none; }
.radar-cd-track { flex: 1; height: 0.4rem; background: var(--bg-well); border: 1px solid var(--line-hot); border-radius: 3px; overflow: hidden; }
.radar-cd-fill { display: block; height: 100%; background: var(--amber); box-shadow: 0 0 0.4em rgba(255,194,75,0.5); transition: width 240ms linear; }
.radar-cd > b { color: var(--amber); font-size: 0.66rem; }
.radar-cd > b.ready { color: var(--green); letter-spacing: 0.06em; }
@media (max-width: 640px) { .radar-pop { right: 0.4rem; bottom: 0.5rem; } }

/* ============================================================================
   WEB UI HOST — shop / remort dialogs (WEB_UI_PLAN.md Phase 0+1)
   Positioned with left/top only (no translate centering) so drag is stable.
   ============================================================================ */
.uih-pop {
  position: fixed; z-index: 45; /* fixed (like the spectate popup) so it drags freely anywhere on screen */
  left: 8px; top: 8px; /* JS recenters on open; drag overwrites */
  width: min(920px, calc(100% - 1.5rem));
  max-height: min(82vh, 720px);
  min-width: 320px;
  min-height: 240px;
  display: flex; flex-direction: column;
  background: linear-gradient(165deg, rgba(8, 14, 12, 0.97), rgba(4, 8, 7, 0.98));
  border: 1px solid rgba(77, 242, 162, 0.35);
  border-radius: 6px;
  box-shadow: 0 0 0 1px rgba(77, 242, 162, 0.12), 0 1.4rem 3rem rgba(0, 0, 0, 0.65),
    inset 0 0 2.5em -1em rgba(77, 242, 162, 0.2);
  /* Popup text has its own DISPLAY > UI size slider; inner text sizes are `em` relative to this base. */
  font-family: var(--mono); color: var(--fg); font-size: var(--uih-size, 16px);
  overflow: hidden; /* outer never scrolls — only .uih-scroll */
  box-sizing: border-box;
}
/* Resize grips for floating windows (shop UI host + snoop). Shared `.pop-rsz` class. */
.pop-rsz {
  position: absolute; z-index: 6;
  touch-action: none; user-select: none;
  background: transparent;
}
.pop-rsz.n  { top: 0; left: 10px; right: 10px; height: 6px; cursor: ns-resize; }
.pop-rsz.s  { bottom: 0; left: 10px; right: 10px; height: 6px; cursor: ns-resize; }
.pop-rsz.e  { right: 0; top: 10px; bottom: 10px; width: 6px; cursor: ew-resize; }
.pop-rsz.w  { left: 0; top: 10px; bottom: 10px; width: 6px; cursor: ew-resize; }
.pop-rsz.ne { top: 0; right: 0; width: 12px; height: 12px; cursor: nesw-resize; }
.pop-rsz.nw { top: 0; left: 0; width: 12px; height: 12px; cursor: nwse-resize; }
.pop-rsz.se { bottom: 0; right: 0; width: 16px; height: 16px; cursor: nwse-resize; }
.pop-rsz.sw { bottom: 0; left: 0; width: 12px; height: 12px; cursor: nesw-resize; }
/* Visible SE corner chevron — the main resize affordance. */
.pop-rsz.se::before,
.pop-rsz.se::after {
  content: '';
  position: absolute;
  right: 3px; bottom: 3px;
  border-right: 2px solid rgba(77, 242, 162, 0.55);
  border-bottom: 2px solid rgba(77, 242, 162, 0.55);
  pointer-events: none;
}
.pop-rsz.se::before { width: 9px; height: 9px; opacity: 0.9; }
.pop-rsz.se::after  { width: 5px; height: 5px; right: 3px; bottom: 3px; opacity: 0.55; }
.uih-pop:hover .pop-rsz.se::before,
.snoop-pop:hover .pop-rsz.se::before,
.uih-pop.resizing .pop-rsz.se::before,
.snoop-pop.resizing .pop-rsz.se::before {
  border-color: rgba(77, 242, 162, 0.9);
}
.uih-pop.resizing,
.snoop-pop.resizing {
  user-select: none;
  box-shadow: 0 0 0 1px rgba(77, 242, 162, 0.35), 0 1.4rem 3rem rgba(0, 0, 0, 0.65),
    inset 0 0 2.5em -1em rgba(77, 242, 162, 0.28);
}
.uih-head {
  display: flex; align-items: center; gap: 0.55ch; padding: 0.5rem 0.7rem;
  background: rgba(77, 242, 162, 0.07); border-bottom: 1px solid var(--line-hot);
  font-size: 0.72em; letter-spacing: 0.12em; touch-action: none; cursor: grab;
  flex: none; user-select: none;
}
.uih-led {
  width: 0.5rem; height: 0.5rem; flex: none; border-radius: 50%; background: var(--amber);
  box-shadow: 0 0 0.5em var(--amber);
}
.uih-title { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--green); }
.uih-head .grow { flex: 1; }
button.uih-x {
  cursor: pointer; color: var(--fg-dim); font-size: 1.2em; line-height: 1;
  padding: 0.15rem 0.4rem; margin: 0; border: 0; background: transparent;
  font-family: inherit; border-radius: 3px; flex: none;
}
button.uih-x:hover { color: var(--red); background: rgba(255, 77, 77, 0.12); }
.uih-body {
  flex: 1 1 auto; min-height: 0; /* critical for nested flex scroll */
  display: flex; flex-direction: column; gap: 0;
  padding: 0; overflow: hidden;
}
.uih-top {
  flex: none; padding: 0.55rem 0.75rem 0.45rem;
  display: flex; flex-direction: column; gap: 0.45rem;
  border-bottom: 1px solid var(--line-hot);
  background: rgba(0, 0, 0, 0.15);
}
.uih-scroll {
  flex: 1 1 auto; min-height: 0;
  overflow-x: hidden; overflow-y: auto;
  overscroll-behavior: contain; /* don't chain-scroll the game stream */
  padding: 0.35rem 0.75rem;
  -webkit-overflow-scrolling: touch;
}
.uih-footblock {
  flex: none; padding: 0.45rem 0.75rem 0.55rem;
  border-top: 1px solid var(--line-hot);
  background: rgba(0, 0, 0, 0.18);
  display: flex; flex-direction: column; gap: 0.4rem;
  max-height: 44%; overflow: auto; overscroll-behavior: contain;
}
/* (shop) draggable SPLITTER between the buy table and the sell/footer — lets the player give the
   SELL-FROM-INVENTORY list more room (it otherwise showed only ~3 rows). Double-click resets. */
.uih-split {
  flex: none; height: 10px; position: relative; cursor: ns-resize;
  touch-action: none; user-select: none;
  background: rgba(0, 0, 0, 0.22);
  border-top: 1px solid var(--line-hot); border-bottom: 1px solid var(--line-hot);
}
.uih-split::before {
  content: ''; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 34px; height: 2px; border-radius: 2px; background: rgba(77, 242, 162, 0.4);
  box-shadow: 0 -3px 0 rgba(77, 242, 162, 0.28), 0 3px 0 rgba(77, 242, 162, 0.28);
}
.uih-split:hover::before, .uih-split.dragging::before { background: rgba(77, 242, 162, 0.9); }
/* while a user height is applied (.sized) the footer fills exactly that height and the sell list
   becomes the internal scroller (pager + close stay pinned above/below it). */
.uih-footblock.sized { max-height: none; overflow: hidden; }
.uih-footblock.sized .uih-sell { flex: 1 1 auto; min-height: 0; max-height: none; }
.uih-toast {
  flex: none; padding: 0.4rem 0.75rem; font-size: 0.78em; color: var(--amber);
  background: rgba(255, 194, 75, 0.08); border-top: 1px solid rgba(255, 194, 75, 0.25);
}
.uih-wallet {
  display: flex; flex-wrap: wrap; gap: 0.8rem 1.4rem; padding: 0.4rem 0.55rem;
  background: rgba(77, 242, 162, 0.05); border: 1px solid var(--line-hot); border-radius: 4px;
  font-size: 0.78em;
}
.uih-wallet i { color: var(--fg-faint); font-style: normal; letter-spacing: 0.1em; font-size: 0.62em; margin-right: 0.5ch; }
.uih-wallet b { color: var(--green); font-weight: 700; }
.uih-search {
  display: flex; align-items: center; gap: 0.35rem;
  padding: 0.15rem 0.15rem 0.15rem 0.55rem;
  background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-hot); border-radius: 4px;
}
.uih-search:focus-within { border-color: var(--green); box-shadow: 0 0 0 1px rgba(77, 242, 162, 0.25); }
.uih-search-in {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--mono); font-size: 0.78em; color: var(--fg);
  background: transparent; border: 0; outline: none; padding: 0.35rem 0;
  -webkit-appearance: none; appearance: none;
}
.uih-search-in::placeholder { color: var(--fg-faint); letter-spacing: 0.02em; }
/* Hide native search clear (we use our own × so styling is consistent). */
.uih-search-in::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
button.uih-search-x {
  flex: none; font-family: var(--mono); font-size: 1em; line-height: 1;
  width: 1.6rem; height: 1.6rem; padding: 0; cursor: pointer;
  color: var(--fg-dim); background: transparent; border: 0; border-radius: 3px;
}
button.uih-search-x:hover { color: var(--red); background: rgba(255, 77, 77, 0.12); }
button.uih-search-x.hidden { visibility: hidden; pointer-events: none; }
.uih-chips {
  display: flex; flex-wrap: wrap; gap: 0.35rem;
  max-height: 4.5rem; overflow-y: auto; overscroll-behavior: contain;
}
.uih-chip {
  font-family: var(--mono); font-size: 0.66em; letter-spacing: 0.08em; font-weight: 700;
  padding: 0.28rem 0.55rem; border-radius: 3px; cursor: pointer;
  color: var(--fg-dim); background: transparent; border: 1px solid var(--line-hot);
}
.uih-chip:hover { color: var(--fg); border-color: var(--line-lock); }
.uih-chip.on { color: #03170d; background: var(--green); border-color: var(--green); }

/* ===== itemdb / changes browser popups (web `itemdb`, `changes`) ============================= */
.uih-row-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.8rem; }
.uih-select { font-family: var(--mono); font-size: 0.68em; letter-spacing: 0.04em; color: var(--fg);
  background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-hot); border-radius: 3px; padding: 0.28rem 0.4rem; cursor: pointer; }
.uih-select:focus { outline: none; border-color: var(--green); }
.uih-count { color: var(--fg-faint); font-size: 0.68em; letter-spacing: 0.06em; margin-left: auto; }
.uih-legend { display: flex; flex-wrap: wrap; gap: 0.3rem 1rem; font-size: 0.68em; color: var(--fg-faint); }
.uih-legend b { text-transform: uppercase; letter-spacing: 0.08em; margin-right: 0.3ch; font-size: 0.94em; }
.uih-legend .lg-loot { color: #8fb8ff; }
.uih-legend .lg-world { color: var(--amber); }
/* pager row (pinned under the scroll list) */
.uih-pager { flex: none; display: flex; align-items: center; justify-content: center; gap: 0.8rem;
  padding: 0.4rem 0.75rem; border-top: 1px solid var(--line-hot); background: rgba(0, 0, 0, 0.18); }
.uih-pager:empty { display: none; }
.uih-page-btn { font-family: var(--mono); font-size: 0.72em; color: var(--fg-dim);
  background: transparent; border: 1px solid var(--line-hot); border-radius: 3px; padding: 0.3rem 0.7rem; cursor: pointer; }
.uih-page-btn:hover:not([disabled]) { color: var(--fg); border-color: var(--green); }
.uih-page-btn[disabled] { opacity: 0.35; cursor: default; }
.uih-page-n { color: var(--fg-faint); font-size: 0.7em; letter-spacing: 0.06em; }
.faint { color: var(--fg-faint); }

/* itemdb rows (click to expand detail) */
.uih-item { border: 1px solid var(--line); border-radius: 4px; margin-bottom: 0.4rem; background: var(--bg-well); }
.uih-item.open { border-color: var(--line-lock); }
.uih-item-hd { display: flex; align-items: center; gap: 0.5rem 0.8rem; flex-wrap: wrap;
  padding: 0.45rem 0.6rem; cursor: pointer; }
.uih-item-hd:hover { background: rgba(255, 255, 255, 0.03); }
.uih-item-name { font-weight: 600; overflow-wrap: anywhere; }
.uih-item-badges { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-left: auto; }
.uih-badge { font-family: var(--mono); font-size: 0.6em; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 0.14rem 0.4rem; border-radius: 3px; border: 1px solid var(--line-hot); color: var(--fg-dim); white-space: nowrap; }
.uih-badge.eq { color: var(--green); border-color: var(--green-dim); text-transform: none; }
.uih-badge.world { color: var(--amber); border-color: var(--amber); }
.uih-badge.stat { color: #8fb8ff; border-color: #38507a; }
.uih-badge.stat.neg { color: var(--red); border-color: var(--red); }
.uih-badge.bound { color: #ffe08a; border-color: #9c6b20; background: rgba(92, 51, 8, 0.72); }
.uih-badge.bindwarn { color: #ffd3a0; border-color: #8f5a31; background: rgba(74, 37, 15, 0.62); }
.uih-bound-note { margin-top: 0.38rem; padding: 0.42rem 0.55rem; color: #ffe8b8;
  border: 1px solid rgba(218, 154, 55, 0.48); border-radius: 0.32rem;
  background: rgba(42, 25, 9, 0.82); font-size: 0.82em; line-height: 1.35; }
.uih-item-detail { padding: 0.1rem 0.7rem 0.6rem; border-top: 1px solid var(--line); }
.uih-drow { display: flex; gap: 0.6rem; padding: 0.22rem 0; font-size: 0.8em; }
.uih-dk { color: var(--fg-faint); min-width: 5.5rem; flex: none; letter-spacing: 0.03em; }
.uih-dv { color: var(--fg-dim); flex: 1; min-width: 0; overflow-wrap: anywhere; }
.uih-rate { font-weight: 700; font-variant-numeric: tabular-nums; }
.uih-rate.loot { color: #8fb8ff; }
.uih-rate.world { color: var(--amber); }
.uih-flag { display: inline-block; font-size: 0.66em; color: var(--fg-faint);
  border: 1px solid var(--line-hot); border-radius: 3px; padding: 0.08rem 0.35rem; margin: 0 0.25rem 0.25rem 0; }
.uih-item-desc { color: var(--fg-faint); font-style: italic; font-size: 0.8em; margin-top: 0.4rem; line-height: 1.4; }

/* changelog rows */
.uih-datef { display: flex; align-items: center; gap: 0.3rem; font-size: 0.62em; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--fg-faint); }
.uih-date { font-family: var(--mono); font-size: 0.74em; color: var(--fg); background: rgba(0, 0, 0, 0.28);
  border: 1px solid var(--line-hot); border-radius: 3px; padding: 0.25rem 0.35rem; }
.uih-date:focus { outline: none; border-color: var(--green); }
.uih-chg { border: 1px solid var(--line); background: var(--bg-well); padding: 0.45rem 0.65rem; margin-bottom: 0.45rem; }
.uih-chg-date { font-family: var(--ui); font-weight: 600; font-size: 0.66em; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--amber); margin-bottom: 0.25rem; }
.uih-chg-cid { color: var(--fg-faint); margin-left: 0.8ch; letter-spacing: 0.06em; }
.uih-chg-body { color: var(--fg-dim); white-space: pre-wrap; word-break: break-word; font-size: 0.86em; line-height: 1.45; }

/* ===== inventory / equipment manager popup ================================================= */
.uih-inv-sum { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1.2rem; }
.uih-inv-wt { display: flex; align-items: center; gap: 0.5rem; flex: 1 1 12rem; min-width: 10rem; font-size: 0.76em; }
.uih-inv-wlab { color: var(--fg-faint); letter-spacing: 0.1em; font-size: 0.6em; }
.uih-inv-wbar { flex: 1 1 auto; height: 0.5rem; background: rgba(0, 0, 0, 0.35); border: 1px solid var(--line-hot); border-radius: 3px; overflow: hidden; }
.uih-inv-wbar i { display: block; height: 100%; background: var(--green); }
.uih-inv-wt.over .uih-inv-wbar i { background: var(--red); }
.uih-inv-wt b { color: var(--fg-dim); font-variant-numeric: tabular-nums; }
.uih-inv-wt.over b { color: var(--red); }
.uih-inv-counts { display: flex; gap: 1rem; font-size: 0.72em; }
.uih-inv-counts i { color: var(--fg-faint); font-style: normal; letter-spacing: 0.08em; margin-right: 0.4ch; font-size: 0.9em; }
.uih-inv-counts b { color: var(--green); }
.uih-inv-h { font-family: var(--ui); font-size: 0.66em; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--amber); margin: 0.5rem 0 0.35rem; padding-bottom: 0.2rem; border-bottom: 1px solid var(--line-hot); }
.uih-inv-eq { display: flex; flex-direction: column; gap: 0.25rem; margin-bottom: 0.4rem; }
.uih-inv-eqrow { display: flex; align-items: center; gap: 0.6rem; padding: 0.35rem 0.5rem; border: 1px solid var(--line); border-radius: 4px; background: var(--bg-well); }
.uih-inv-slot { color: var(--fg-faint); font-size: 0.62em; text-transform: uppercase; letter-spacing: 0.06em; min-width: 5rem; flex: none; }
.uih-inv-eqrow .uih-item-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.uih-inv-eqrow.empty { opacity: 0.55; border-style: dashed; }
.uih-inv-empty { flex: 1; color: var(--fg-faint); font-style: italic; font-size: 0.82em; }
.uih-inv-item .uih-item-hd { cursor: default; }
.uih-inv-actions { display: flex; align-items: center; gap: 0.3rem; margin-left: auto; flex: none; }
.uih-mini { font-family: var(--mono); font-size: 0.64em; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 0.22rem 0.5rem; border-radius: 3px; cursor: pointer; color: #03170d; background: var(--green); border: 1px solid var(--green); }
.uih-mini:hover { filter: brightness(1.12); }
.uih-mini.danger { color: var(--fg); background: transparent; border-color: var(--line-lock); }
.uih-mini.danger:hover { color: var(--red); border-color: var(--red); }
.uih-qty { width: 3.2rem; font-family: var(--mono); font-size: 0.72em; color: var(--fg);
  background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-hot); border-radius: 3px; padding: 0.18rem 0.3rem; }
.uih-badge.qty { color: var(--amber); border-color: var(--amber); }
/* compare-vs-equipped floating tooltip */
.uih-cmp { position: fixed; z-index: 10050; min-width: 12rem; padding: 0.45rem 0.6rem;
  background: var(--bg-deep, #0a0f14); border: 1px solid var(--green-dim); border-radius: 5px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5); pointer-events: none; font-size: var(--uih-size, 14px); }
.uih-cmp-hd { display: flex; justify-content: space-between; gap: 1rem; color: var(--fg-faint);
  font-size: 0.6em; letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 0.3rem; padding-bottom: 0.25rem; border-bottom: 1px solid var(--line-hot); }
.uih-cmp-row { display: grid; grid-template-columns: 4.5rem 1fr 1rem 1fr; align-items: center; gap: 0.3rem; padding: 0.1rem 0; }
.uih-cmp-k { color: var(--fg-faint); }
.uih-cmp-v { color: var(--fg); font-variant-numeric: tabular-nums; text-align: right; }
.uih-cmp-v.old { color: var(--fg-dim); }
.uih-cmp-vs { text-align: center; font-weight: 700; }
.uih-cmp-vs.up { color: var(--green); }
.uih-cmp-vs.dn { color: var(--red); }
.uih-cmp-vs.eq { color: var(--amber); }

/* ===== score / top / history / mail / help popups ========================================== */
.uih-score { padding: 0.7rem 0.85rem; }
.uih-score-hero { border: 1px solid var(--green-dim); border-radius: 6px; padding: 0.7rem 0.85rem; margin-bottom: 0.7rem;
  background: radial-gradient(120% 130% at 0% 0%, rgba(77, 242, 162, 0.09), transparent 60%), var(--bg-well); }
.uih-score-name { font-size: 1.5em; font-weight: 800; line-height: 1.1; }
.uih-score-sub { color: var(--fg-dim); font-size: 0.86em; margin-top: 0.2rem; }
.uih-score-plbig { margin-top: 0.6rem; display: flex; align-items: baseline; gap: 0.7ch; flex-wrap: wrap; }
.uih-score-plbig i { color: var(--fg-faint); font-style: normal; font-size: 0.62em; letter-spacing: 0.16em; }
.uih-score-plbig b { color: var(--green); font-size: 1.6em; font-weight: 800; font-variant-numeric: tabular-nums; text-shadow: 0 0 14px rgba(77, 242, 162, 0.4); }
.uih-score-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr)); gap: 0.5rem; margin-bottom: 0.6rem; }
.uih-score-stat { border: 1px solid var(--line); border-radius: 5px; background: var(--bg-well); padding: 0.5rem 0.6rem; display: flex; flex-direction: column; gap: 0.15rem; }
.uih-score-lbl { color: var(--fg-faint); font-style: normal; font-size: 0.62em; letter-spacing: 0.12em; text-transform: uppercase; }
.uih-score-num { color: var(--fg); font-size: 1.15em; font-weight: 700; font-variant-numeric: tabular-nums; }
.uih-score-bn { color: var(--green); font-size: 0.74em; font-weight: 600; min-height: 1em; }
.uih-score-bn.empty { color: transparent; }
.uih-score-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 0.15rem 0.9rem; margin-bottom: 0.5rem; }
.uih-score-cell { display: flex; justify-content: space-between; gap: 0.6rem; font-size: 0.82em; border-bottom: 1px solid var(--line); padding: 0.28rem 0; }
.uih-score-cell i { color: var(--fg-faint); font-style: normal; }
.uih-score-cell b { color: var(--fg); font-variant-numeric: tabular-nums; text-align: right; }
/* top */
.uih-top-list { padding: 0.35rem 0.5rem; }
.uih-top-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.3rem 0.5rem; border-bottom: 1px solid var(--line); font-size: 0.85em; }
.uih-top-row.me { background: rgba(77, 242, 162, 0.08); border-radius: 4px; }
.uih-top-rank { color: var(--fg-faint); min-width: 2.5rem; font-variant-numeric: tabular-nums; }
.uih-top-name { font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.uih-top-race { color: var(--fg-faint); font-size: 0.75em; min-width: 5rem; text-align: right; }
.uih-top-metric { color: var(--green); font-variant-numeric: tabular-nums; min-width: 6rem; text-align: right; }
/* history */
.uih-hist { padding: 0.65rem 0.75rem; }
.uih-hist-row {
  padding: 0.65rem 0.75rem; margin-bottom: 0.55rem; border: 1px solid var(--line);
  border-left: 3px solid #65bfff; border-radius: 7px; background: var(--bg-well);
  font-size: 0.9em; line-height: 1.5;
}
.uih-hist-row:last-child { margin-bottom: 0; }
.uih-hist-meta { display: flex; align-items: center; gap: 0.55rem; min-width: 0; margin-bottom: 0.35rem; }
.uih-hist-channel {
  flex: none; padding: 0.1rem 0.38rem; border: 1px solid rgba(101,191,255,.45); border-radius: 999px;
  color: #b9e2ff; background: rgba(70,164,235,.12); font-size: 0.62em; font-weight: 800; letter-spacing: .09em;
}
.uih-hist-sender { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 1.02em; font-weight: 750; }
.uih-hist-date, .uih-hist-mid { flex: none; color: var(--fg-faint); font-size: 0.68em; font-variant-numeric: tabular-nums; }
.uih-hist-mid { padding-left: .35rem; border-left: 1px solid var(--line-hot); }
.uih-hist-msg { color: var(--fg); word-break: break-word; overflow-wrap: anywhere; white-space: pre-wrap; }
/* mail */
.uih-mail-hd { display: flex; align-items: center; justify-content: space-between; gap: 1rem; font-size: 0.8em; color: var(--fg-dim); }
.uih-mail { padding: 0.4rem 0.6rem; }
.uih-mail-row { border: 1px solid var(--line); border-radius: 4px; margin-bottom: 0.4rem; background: var(--bg-well); }
.uih-mail-row.unread { border-color: var(--amber); }
.uih-mail-rowhd { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0.6rem; cursor: pointer; font-size: 0.84em; }
.uih-mail-rowhd:hover { background: rgba(255, 255, 255, 0.03); }
.uih-mail-from { font-weight: 600; min-width: 8rem; overflow: hidden; text-overflow: ellipsis; }
.uih-mail-subj { flex: 1; color: var(--fg-dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.uih-mail-when { color: var(--fg-faint); font-size: 0.7em; }
.uih-mail-body { padding: 0.2rem 0.6rem 0.6rem; border-top: 1px solid var(--line); }
.uih-mail-text { color: var(--fg-dim); white-space: pre-wrap; word-break: break-word; font-size: 0.85em; margin-bottom: 0.5rem; line-height: 1.45; }
.uih-mail-compose { padding: 0.4rem 0.2rem; }
.uih-mail-field { display: flex; align-items: center; gap: 0.5rem; margin: 0.3rem 0; }
.uih-mail-field label { color: var(--fg-faint); font-size: 0.72em; min-width: 2rem; }
.uih-mail-to { flex: 1; border: 1px solid var(--line-hot); border-radius: 3px; padding: 0.35rem 0.5rem; background: rgba(0,0,0,0.28); }
.uih-mail-body-in { width: 100%; min-height: 6rem; resize: vertical; font-family: var(--mono); font-size: 0.85em;
  color: var(--fg); background: rgba(0, 0, 0, 0.28); border: 1px solid var(--line-hot); border-radius: 4px; padding: 0.5rem; margin: 0.3rem 0; }
.uih-mail-body-in:focus, .uih-mail-to:focus { outline: none; border-color: var(--green); }
.uih-gift-box {
  margin: .7rem 0; padding: .6rem .7rem; border: 1px solid rgba(255,194,75,.24);
  border-left: 2px solid var(--amber); background: rgba(255,194,75,.035);
}
.uih-gift-box .uih-inv-h { margin-top: 0; }
.uih-gift-picker { display: grid; grid-template-columns: minmax(0,1fr) 4rem auto; gap: .4rem; align-items: center; }
.uih-gift-select { min-width: 0; width: 100%; }
.uih-gift-select {
  color-scheme: dark; color: var(--fg); background: #07100d;
  border-color: rgba(110,225,169,.32); padding-block: .38rem;
}
.uih-gift-select option {
  color: #eef7f2; background: #07100d; font-family: var(--mono);
}
.uih-gift-select option:checked { color: #04110b; background: var(--green); }
.uih-gift-qty { width: 100%; box-sizing: border-box; }
.uih-gift-attached { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .55rem; min-height: 1.5rem; align-items: center; }
.uih-gift-chip {
  display: inline-flex; align-items: center; gap: .45rem; padding: .2rem .35rem .2rem .5rem;
  border: 1px solid rgba(255,194,75,.3); color: var(--fg); background: rgba(0,0,0,.2); font-size: .72em;
}
.uih-gift-remove { padding: 0 .2rem; border: 0; background: transparent; color: var(--fg-faint); cursor: pointer; font: inherit; }
.uih-gift-remove:hover { color: var(--red); }
.uih-gift-note { margin-top: .45rem; color: var(--fg-faint); font-size: .7em; line-height: 1.4; }
.uih-gift-result { min-height: 1.2rem; margin: .35rem 0; color: var(--amber); font-size: .74em; }
.uih-gift-result.err { color: var(--red); }
@media (max-width: 520px) {
  .uih-gift-picker { grid-template-columns: minmax(0,1fr) 3.5rem; }
  .uih-gift-picker .uih-btn { grid-column: 1 / -1; }
}
/* help */
.uih-help { padding: 0.4rem 0.6rem; }
.uih-help-row { border-bottom: 1px solid var(--line); }
.uih-help-hd { display: flex; align-items: baseline; gap: 0.8rem; padding: 0.35rem 0.3rem; cursor: pointer; }
.uih-help-hd:hover { background: rgba(255, 255, 255, 0.03); }
.uih-help-hd b { color: var(--green); font-weight: 600; }
.uih-help-usage { color: var(--fg-faint); font-size: 0.74em; font-family: var(--mono); }
.uih-help-body { color: var(--fg-dim); white-space: pre-wrap; word-break: break-word; font-size: 0.85em; line-height: 1.5; padding: 0.2rem 0.6rem 0.6rem; }
/* clickable player name (rankings / OOC history) + its action menu */
.uih-nameclick { cursor: pointer; border-bottom: 1px dotted rgba(255, 255, 255, 0.25); }
.uih-nameclick:hover { border-bottom-color: var(--green); filter: brightness(1.15); }
.uih-namemenu { position: fixed; z-index: 10060; min-width: 8rem; padding: 0.25rem; font-size: var(--uih-size, 15px); display: flex; flex-direction: column;
  background: var(--bg-deep, #0a0f14); border: 1px solid var(--green-dim); border-radius: 5px; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.55); }
.uih-nm-title { color: var(--fg-faint); font-size: 0.72em; letter-spacing: 0.06em; padding: 0.2rem 0.5rem 0.3rem; border-bottom: 1px solid var(--line-hot); margin-bottom: 0.2rem; overflow: hidden; text-overflow: ellipsis; }
.uih-nm-item { text-align: left; font-family: var(--mono); font-size: 0.85em; color: var(--fg-dim); background: transparent; border: 0; border-radius: 3px; padding: 0.35rem 0.5rem; cursor: pointer; }
.uih-nm-item:hover { color: var(--fg); background: rgba(77, 242, 162, 0.12); }
/* (mail compose) custom in-UI name autocomplete dropdown */
.uih-ac { position: fixed; z-index: 10070; font-size: var(--uih-size, 15px); max-height: 14rem; overflow-y: auto; padding: 0.2rem;
  background: var(--bg-deep, #0a0f14); border: 1px solid var(--green-dim); border-radius: 5px; box-shadow: 0 8px 26px rgba(0, 0, 0, 0.6); }
.uih-ac-item { font-family: var(--mono); font-size: 0.86em; color: var(--fg-dim); padding: 0.32rem 0.55rem; border-radius: 3px; cursor: pointer; }
.uih-ac-item:hover { color: #03170d; background: var(--green); }
/* skills — a card grid grouped learned / learnable / locked, with mastery + exp bar */
.uih-skills { padding: 0.35rem 0.5rem; }
.uih-skill-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.45rem; margin-bottom: 0.5rem; }
.uih-skill-card { border: 1px solid var(--line); border-radius: 5px; background: var(--bg-well); padding: 0.45rem 0.55rem; display: flex; flex-direction: column; gap: 0.28rem; }
.uih-skill-card.learned { border-color: var(--green-dim); }
.uih-skill-card.locked { opacity: 0.6; }
.uih-skill-name { font-weight: 600; color: var(--fg); overflow-wrap: anywhere; font-size: 0.9em; display: flex; align-items: baseline; gap: 0.4ch; flex-wrap: wrap; }
.uih-skill-carry { font-size: 0.62em; letter-spacing: 0.06em; text-transform: uppercase; color: var(--gold);
  border: 1px solid var(--gold); border-radius: 3px; padding: 0.02rem 0.3rem; font-weight: 700; }
.uih-skill-card.learned .uih-skill-name { color: var(--green); }
.uih-skill-lvl { font-size: 0.66em; letter-spacing: 0.08em; text-transform: uppercase; }
.uih-skill-lvl.learned { color: var(--amber); }
.uih-skill-lvl.locked { color: var(--red); }
.uih-skill-lvl.learnable { color: #8fb8ff; }
.uih-skill-bar { height: 0.35rem; background: rgba(0, 0, 0, 0.35); border: 1px solid var(--line-hot); border-radius: 3px; overflow: hidden; }
.uih-skill-bar i { display: block; height: 100%; background: var(--amber); }
.uih-skill-bar.max i { background: var(--green); }
.uih-skill-exp { font-size: 0.62em; color: var(--fg-faint); font-variant-numeric: tabular-nums; }
.uih-score-bonus { color: var(--green); font-size: 0.82em; }
.uih-quest { padding: 0.5rem 0.6rem; }
.uih-quest-row { border: 1px solid var(--line); border-radius: 4px; background: var(--bg-well); padding: 0.45rem 0.6rem; margin-bottom: 0.45rem; }
.uih-quest-row.done { border-color: var(--green-dim); }
.uih-quest-hd { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; cursor: pointer; }
.uih-quest-hd b { color: var(--fg); flex: 1 1 auto; }
.uih-quest-caret { color: var(--fg-dim); font-family: monospace; width: 1em; text-align: center; flex: 0 0 auto; }
.uih-quest-row.open { border-color: var(--accent, var(--amber)); }
.uih-quest-hd:hover b { color: var(--accent, var(--amber)); }
.uih-quest-status { font-size: 0.66em; letter-spacing: 0.1em; color: var(--amber); flex: 0 0 auto; }
.uih-quest-status.done { color: var(--green); }
.uih-quest-detail { margin-top: 0.4rem; padding-top: 0.4rem; border-top: 1px solid var(--line); }
.uih-quest-desc { color: var(--fg-dim); font-size: 0.85em; margin-bottom: 0.4rem; line-height: 1.45; white-space: pre-wrap; }
.uih-quest-obj { display: flex; align-items: flex-start; gap: 0.5rem; font-size: 0.85em; color: var(--fg); padding: 0.12rem 0; line-height: 1.4; }
.uih-quest-obj.done { color: var(--fg-dim); }
.uih-quest-mark { flex: 0 0 auto; width: 1em; text-align: center; font-family: monospace; color: var(--amber); }
.uih-quest-mark.done { color: var(--green); }

.uih-table {
  display: flex; flex-direction: column; gap: 0;
  border: 1px solid var(--line-hot); border-radius: 4px; overflow: hidden;
}
.uih-tr {
  display: grid; grid-template-columns: 2.4rem minmax(0, 1fr) 5.5rem 5.5rem 3.5rem 5rem;
  gap: 0.4rem; align-items: center; padding: 0.38rem 0.5rem;
  font-size: 0.74em; border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.uih-tr:last-child { border-bottom: none; }
.uih-tr.head {
  position: sticky; top: 0; z-index: 1;
  background: rgba(6, 12, 10, 0.96); color: var(--fg-faint); font-size: 0.62em;
  letter-spacing: 0.1em; font-weight: 700;
  border-bottom: 1px solid var(--line-hot);
}
.uih-tr:not(.head):hover { background: rgba(77, 242, 162, 0.04); }
/* clickable sort headers */
.uih-tr.head .c-head { cursor: pointer; user-select: none; white-space: nowrap; }
.uih-tr.head .c-head:hover { color: var(--fg); }
.uih-tr.head .c-head.sorted { color: var(--green); }
.uih-tr .c-id { color: var(--cyan, #6ecbff); font-weight: 700; }
.uih-tr .c-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
  display: flex; align-items: center; gap: 0.3ch;
}
.uih-tr .c-slot { color: var(--fg-dim); text-transform: lowercase; font-size: 0.68em; }
.uih-tr .c-price { color: var(--amber); font-weight: 700; text-align: right; }
.uih-tr .c-stock { color: var(--fg-dim); text-align: right; }
.uih-tr .c-act { display: flex; gap: 0.3rem; justify-content: flex-end; flex-wrap: nowrap; }
/* BUY fills the action column now that INFO is gone (bigger tap target). */
.uih-tr .c-act .uih-btn.buy { width: 100%; }
/* entity-link chips inside the shop table */
.uih-item-link {
  display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
  vertical-align: bottom; cursor: pointer;
}
.uih-btn {
  font-family: var(--mono); font-size: 0.66em; font-weight: 700; letter-spacing: 0.06em;
  padding: 0.28rem 0.5rem; border-radius: 3px; cursor: pointer;
  color: var(--fg); background: transparent; border: 1px solid var(--line-hot);
}
.uih-btn:hover { border-color: var(--line-lock); }
.uih-btn.buy { color: #03170d; background: var(--green); border-color: var(--green); }
.uih-btn.buy:hover { filter: brightness(1.08); }
.uih-btn.sell { color: #1a1203; background: var(--amber); border-color: var(--amber); }
.uih-btn.ghost { color: var(--fg-dim); }
.uih-btn:disabled { opacity: 0.35; pointer-events: none; }
.uih-pager {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  font-size: 0.72em; color: var(--fg-dim);
}
.uih-page-label { flex: 1; text-align: center; letter-spacing: 0.04em; }
.uih-sell {
  border: 1px solid rgba(255, 194, 75, 0.22); border-radius: 4px; padding: 0.4rem 0.5rem;
  background: rgba(255, 194, 75, 0.04); display: flex; flex-direction: column; gap: 0.2rem;
  max-height: 11rem; overflow: auto; overscroll-behavior: contain;
}
.uih-sell-hd {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  position: sticky; top: 0; z-index: 1;
  background: rgba(18, 14, 6, 0.92); padding-bottom: 0.15rem; margin-bottom: 0.05rem;
}
.uih-sell-hd .uih-sec { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.uih-btn.uih-sell-all { font-size: 0.64em; letter-spacing: 0.04em; white-space: nowrap; flex: none; }
.uih-btn.uih-sell-all.armed {
  color: #1a0808; background: var(--red, #ff4d4d); border-color: var(--red, #ff4d4d);
  animation: uih-arm-pulse 0.9s ease-in-out infinite alternate;
}
@keyframes uih-arm-pulse {
  from { filter: brightness(1); }
  to { filter: brightness(1.12); }
}
.uih-sec { font-size: 0.62em; letter-spacing: 0.14em; color: var(--amber); font-weight: 700; margin-bottom: 0.1rem; }
.uih-sell-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 0.5rem; align-items: center;
  font-size: 0.72em; padding: 0.18rem 0; border-bottom: 1px dotted rgba(255, 255, 255, 0.05);
}
/* stacks get an extra column for the qty stepper (name | price | qty | SELL). */
.uih-sell-row.has-qty { grid-template-columns: minmax(0, 1fr) auto auto auto; }
.uih-sell-row:last-child { border-bottom: none; }
.uih-sell-row .c-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* sell quantity stepper: [−] [ n ] [+] */
.uih-qty {
  display: inline-flex; align-items: stretch; flex: none;
  border: 1px solid var(--line-hot); border-radius: 3px; overflow: hidden;
}
.uih-qbtn {
  font-family: var(--mono); font-size: 0.85em; line-height: 1; cursor: pointer;
  width: 1.5rem; padding: 0; color: var(--fg-dim); background: rgba(255, 255, 255, 0.03); border: 0;
}
.uih-qbtn:hover:not(:disabled) { color: var(--fg); background: rgba(77, 242, 162, 0.12); }
.uih-qbtn:disabled { opacity: 0.35; cursor: default; }
.uih-qin {
  width: 2.6rem; text-align: center; font-family: var(--mono); font-size: 0.72em;
  color: var(--fg); background: rgba(0, 0, 0, 0.25); border: 0; border-left: 1px solid var(--line);
  border-right: 1px solid var(--line); outline: none; padding: 0.2rem 0; -moz-appearance: textfield;
}
.uih-qin::-webkit-outer-spin-button, .uih-qin::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.uih-empty { color: var(--fg-dim); font-size: 0.78em; margin: 0.4rem 0; }
.uih-empty[hidden] { display: none !important; }
.uih-hint { color: var(--fg-faint); font-size: 0.7em; margin: 0; }
.uih-foot { color: var(--fg-faint); font-size: 0.66em; margin: 0; letter-spacing: 0.02em; flex: 1; }
.uih-actions {
  display: flex; align-items: center; gap: 0.6rem; justify-content: space-between;
}

/* ---- Remort wizard ---- */
.uih-pop.kind-remort { border-color: rgba(255, 194, 75, 0.4); }
.uih-pop.kind-remort .uih-title { color: var(--amber); }
.uih-steps {
  display: flex; flex-wrap: wrap; gap: 0.3rem;
}
.uih-step {
  font-family: var(--mono); font-size: 0.62em; letter-spacing: 0.06em; font-weight: 700;
  padding: 0.28rem 0.5rem; border-radius: 3px; cursor: pointer;
  color: var(--fg-dim); background: transparent; border: 1px solid var(--line-hot);
}
.uih-step:hover { color: var(--fg); border-color: var(--line-lock); }
.uih-step.on { color: #1a1203; background: var(--amber); border-color: var(--amber); }
.uih-remort-body { display: flex; flex-direction: column; gap: 0.55rem; padding: 0.55rem 0.75rem; }
.uih-hero-t {
  font-size: 0.95em; font-weight: 700; letter-spacing: 0.12em; color: var(--amber);
  margin-bottom: 0.25rem;
}
.uih-copy { color: var(--fg-dim); font-size: 0.78em; margin: 0; line-height: 1.45; }
.uih-copy.warn { color: var(--amber); }
.uih-hint-hi {
  color: var(--amber); font-size: 0.72em; margin: 0; letter-spacing: 0.04em;
  padding: 0.35rem 0.5rem; border: 1px solid rgba(255,194,75,0.3); border-radius: 3px;
  background: rgba(255,194,75,0.06);
}
.uih-statgrid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.4rem;
}
.uih-statcell {
  padding: 0.4rem 0.45rem; border: 1px solid var(--line-hot); border-radius: 4px;
  background: rgba(0,0,0,0.2); display: flex; flex-direction: column; gap: 0.15rem;
}
.uih-statcell i {
  font-style: normal; font-size: 0.58em; letter-spacing: 0.12em; color: var(--fg-faint);
}
.uih-statcell b { color: var(--green); font-size: 0.82em; font-weight: 700; }
.uih-racegrid {
  display: flex; flex-wrap: wrap; gap: 0.35rem;
}
button.uih-race {
  font-family: var(--mono); font-size: 0.72em; font-weight: 700;
  padding: 0.35rem 0.6rem; border-radius: 3px; cursor: pointer;
  color: var(--fg); background: rgba(77,242,162,0.05); border: 1px solid var(--line-hot);
}
button.uih-race:hover { border-color: var(--green); }
button.uih-race.on { color: #03170d; background: var(--green); border-color: var(--green); }
.uih-wide { width: 100%; justify-content: center; }
.uih-alignrow { display: flex; flex-wrap: wrap; gap: 0.4rem; }
button.uih-align { min-width: 5.5rem; }
.uih-skillist { display: flex; flex-direction: column; gap: 0.3rem; }
.uih-looks-done { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.2rem; }
.uih-lookchip,
button.uih-lookchip {
  font-family: var(--mono); font-size: 0.66em; letter-spacing: 0.04em;
  padding: 0.2rem 0.45rem; border-radius: 3px; cursor: pointer;
  color: var(--fg-dim); border: 1px solid var(--line-hot); background: rgba(0,0,0,0.2);
}
button.uih-lookchip:hover {
  color: var(--fg); border-color: var(--amber); background: rgba(255,194,75,0.08);
}
.uih-edit-row { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.25rem; }
.uih-looks-opts { display: flex; flex-wrap: wrap; gap: 0.35rem; }
button.uih-lookopt { min-width: 4.5rem; }
.uih-summary {
  border: 1px solid rgba(255,194,75,0.25); border-radius: 4px; padding: 0.45rem 0.55rem;
  background: rgba(255,194,75,0.05); display: flex; flex-direction: column; gap: 0.25rem;
}
.uih-sum-row {
  display: flex; justify-content: space-between; gap: 0.75rem; font-size: 0.78em;
}
.uih-sum-row .k { color: var(--fg-faint); letter-spacing: 0.06em; font-size: 0.66em; }
.uih-sum-row .v { color: var(--fg); font-weight: 700; text-align: right; }

@media (max-width: 720px) {
  .uih-pop { width: calc(100% - 0.6rem); max-height: 88vh; }
  .uih-tr { grid-template-columns: 2rem minmax(0, 1fr) 4rem auto; }
  .uih-tr .c-slot, .uih-tr .c-stock { display: none; }
  .uih-tr.head .c-slot, .uih-tr.head .c-stock { display: none; }
  .uih-footblock { max-height: 42%; }
  .uih-statgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ============================================================================
   MOVABLE / RESIZABLE COCKPIT (docked model, desktop only) — drag panel headers
   to reorder, drag a panel's bottom edge to resize its height, drag the rail's
   right edge to set the column width. Per-character (play.js layout engine).
   ========================================================================= */
.rail-split { display: none; }
.play.cockpit > .rail-split {
  display: block; grid-area: rail; justify-self: end; align-self: stretch;
  position: relative; width: 10px; margin-right: -5px; cursor: ew-resize; z-index: 8; touch-action: none;
}
.play.cockpit > .rail-split::after {
  content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 2px; height: 34px; border-radius: 2px;
  background: color-mix(in srgb, var(--line-hot) 45%, transparent); opacity: 0; transition: opacity 120ms;
}
.play.cockpit > .rail-split:hover::after,
body.rail-resizing .play.cockpit > .rail-split::after { opacity: 0.85; }

/* SPLIT-PANE divider — a panel's bottom edge, shown only when there's an expanded panel below it to
   resize against (.has-divider, set by applyPaneSizes). Sits in the 6px gap between the two panels. */
.pr-resize { display: none; }
.play.cockpit .pr-card.has-divider > .pr-resize {
  display: block; position: absolute; left: 0; right: 0; bottom: -4px; height: 9px;
  cursor: ns-resize; z-index: 7; touch-action: none;
}
.play.cockpit .pr-card.has-divider > .pr-resize::after {
  content: ''; position: absolute; left: 50%; bottom: 3px; transform: translateX(-50%);
  width: 30px; height: 2px; border-radius: 2px;
  background: color-mix(in srgb, var(--line-hot) 40%, transparent); opacity: 0; transition: opacity 120ms;
}
.play.cockpit .pr-card.has-divider:hover > .pr-resize::after,
body.rail-resizing .play.cockpit .pr-card.has-divider > .pr-resize::after { opacity: 0.7; }

/* COLLAPSE / EXPAND chevron (top-right of each panel) + the collapsed (header-only) state */
.pr-collapse { display: none; }
.play.cockpit .pr-card > .pr-collapse {
  display: block; position: absolute; top: 2px; right: 3px; z-index: 9;
  width: 15px; height: 15px; padding: 0; border: 0; border-radius: 2px; cursor: pointer;
  background: color-mix(in srgb, var(--bg-well, #080c0b) 62%, transparent);
  color: var(--line-hot); opacity: 0; transition: opacity 120ms;
}
.play.cockpit .pr-card:hover > .pr-collapse { opacity: 0.6; }
.play.cockpit .pr-card > .pr-collapse:hover { opacity: 1; }
.play.cockpit .pr-card > .pr-collapse::before {
  content: ''; position: absolute; left: 50%; top: 50%; width: 6px; height: 6px;
  border-left: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translate(-50%, -70%) rotate(-45deg); /* chevron-up = "collapse" */
}
.play.cockpit .pr-card.collapsed > .pr-collapse::before { transform: translate(-50%, -30%) rotate(135deg); } /* down = "expand" */
/* collapsed = header-only: cap the height so only the top (title / first line) shows; hide the divider. */
.play.cockpit .pr-card.collapsed { max-height: 2.25rem; overflow: hidden; }
.play.cockpit .pr-card.collapsed.standby::after { display: none; }
/* Header + PL + energy must always fit, even while old saved weights are being
   restored on first paint. The JS layout engine enforces the measured floor too. */
.play.cockpit .pr-stats:not(.collapsed) { min-height: 6rem; }

/* header drag affordance — a move cursor on the drag zones; interactive children keep their pointer */
.play.cockpit .pr-hd,
.play.cockpit .ex-line,
.play.cockpit .pr-ctabs,
.play.cockpit .tip-line { cursor: grab; }
.play.cockpit .pr-hd button,
.play.cockpit .pr-ctabs .pr-ctab,
.play.cockpit .ex-line .ex-chip,
.play.cockpit .nb-chip { cursor: pointer; }
.pr-card.pane-drag {
  opacity: 0.92; z-index: 30; box-shadow: 0 8px 26px rgba(0, 0, 0, 0.55);
  outline: 1px solid var(--line-hot); border-radius: 2px;
}
.pr-card.pane-drag .pr-hd,
.pr-card.pane-drag .ex-line,
.pr-card.pane-drag .pr-ctabs,
.pr-card.pane-drag .tip-line { cursor: grabbing; }
body.rail-resizing, body.rail-resizing * { user-select: none !important; }

/* ============================================================================
   TARGET panel (combat focus) — a WoW-style enemy frame: name + HP (enemy-red)
   + stamina. Sense-gated by the server: an "unsensed" HP bar (no precise fill,
   qualitative mark) for viewers without a scouter/sense; a locked stamina bar
   until the viewer can scan it.
   ========================================================================= */
.pr-target .pr-hd .t { color: #ff8f8a; }
.tgt-sense {
  font-family: var(--mono); font-size: 0.58rem; letter-spacing: 0.14em; font-weight: 700;
  color: var(--fg-faint); text-transform: uppercase;
}
.tgt-body { display: flex; flex-direction: column; gap: 4px; padding: 3px 6px 6px; min-width: 0; }
.tgt-name {
  font-family: var(--mono); font-size: 0.9rem; font-weight: 700; color: #ffe0e0; letter-spacing: 0.02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: 0 0 6px rgba(232, 67, 63, 0.35);
}
.tgt-body .stat-row { min-width: 0; }
.tgt-hp-bar { --en-base: #e8433f; }
.tgt-hp-bar .stat-fill { --fill: #e8433f; }
.tgt-st-bar { --en-base: #f0b24a; }
.tgt-st-bar .stat-fill { --fill: #f0b24a; }
/* unsensed HP / locked stamina: drop the misleading precise fill, show a hatched "no read" track */
.pr-target.t-unsensed .tgt-hp-bar .stat-fill,
.pr-target.t-unsensed .tgt-hp-bar .stat-ghost,
.pr-target.t-unsensed .tgt-hp-bar .stat-fill::after,
.pr-target.t-enlock .tgt-st-bar .stat-fill,
.pr-target.t-enlock .tgt-st-bar .stat-ghost,
.pr-target.t-enlock .tgt-st-bar .stat-fill::after { display: none; }
.pr-target.t-unsensed .tgt-hp-bar,
.pr-target.t-enlock .tgt-st-bar {
  background: repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.03) 0 6px, rgba(0, 0, 0, 0.36) 6px 12px), var(--bg-well, #080c0b);
  border-color: color-mix(in srgb, var(--line) 85%, transparent);
}
/* the qualitative mark ("weakened", "an unknown power", "???", "no scan") centered over the empty bar */
.stat-num.sense-mark {
  color: #cfc8ac !important; font-size: 0.64rem !important; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; right: auto !important; left: 50% !important;
  transform: translate(-50%, -50%) !important; text-shadow: 0 0 4px #000, 0 1px 2px #000;
}

/* ============================================================================
   TIPS ticker — the broadcast TIPS channel surfaced in a movable panel.
   ========================================================================= */
.pr-tips .tip-line { display: flex; align-items: center; gap: 8px; padding: 5px 8px; min-width: 0; }
.tip-badge {
  flex: none; font-family: var(--mono); font-size: 0.56rem; font-weight: 700; letter-spacing: 0.14em;
  color: #06160e; background: var(--green); padding: 1px 6px; border-radius: 2px;
}
.tip-text {
  font-family: var(--mono); font-size: 0.74rem; line-height: 1.32; color: var(--fg); min-width: 0;
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
}

/* mobile: the movable layout is desktop-only — clear any inline flex the engine may have left, keep the
   tab-sheet system in charge, re-enable rail scroll for the single full-area sheet, and hide the
   desktop-only handles (divider / collapse chevron / rail splitter). */
@media (max-width: 900px) {
  .play-rail { overflow-y: auto; }
  .play-rail .pr-card { flex: none !important; max-height: none !important; }
  .pr-resize, .pr-collapse, .rail-split { display: none !important; }
}

/* ============================================================================
   POLISHED POPUP SYSTEM — shared by every structured in-game window.
   The host keeps its existing drag, resize, patch and scroll-preservation logic;
   this layer gives all popup kinds one clear visual and interaction language.
   ========================================================================= */
.uih-pop {
  isolation: isolate; border-color: rgba(105, 236, 173, .38); border-radius: 9px;
  background:
    radial-gradient(circle at 15% -20%, rgba(77,242,162,.105), transparent 28rem),
    linear-gradient(155deg, rgba(13, 25, 20, .985), rgba(3, 8, 6, .99));
  box-shadow: 0 0 0 1px rgba(0,0,0,.85), 0 25px 75px rgba(0,0,0,.72),
    0 0 50px rgba(55,220,143,.065), inset 0 1px rgba(183,255,220,.07);
  animation: uih-open 190ms cubic-bezier(.2,.78,.26,1) both;
}
.uih-pop::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    linear-gradient(90deg, rgba(77,242,162,.8), transparent 18%) top left / 90px 1px no-repeat,
    linear-gradient(180deg, rgba(77,242,162,.7), transparent 14%) top left / 1px 65px no-repeat,
    linear-gradient(270deg, rgba(77,242,162,.55), transparent 16%) bottom right / 85px 1px no-repeat,
    linear-gradient(0deg, rgba(77,242,162,.5), transparent 12%) bottom right / 1px 60px no-repeat;
}
.uih-head {
  min-height: 2.75rem; padding: .58rem .72rem; gap: .7rem;
  background: linear-gradient(180deg, rgba(26, 50, 39, .96), rgba(8, 19, 14, .97));
  border-bottom-color: rgba(103,229,169,.23); box-shadow: 0 9px 25px rgba(0,0,0,.24);
}
.uih-led { box-shadow: 0 0 0 3px rgba(255,194,75,.08), 0 0 12px rgba(255,194,75,.72); }
.uih-title { font-size: 1.08em; letter-spacing: .16em; text-shadow: 0 0 14px rgba(77,242,162,.22); }
.uih-channel { color: var(--fg-faint); font-size: .7em; letter-spacing: .12em; white-space: nowrap; }
button.uih-x {
  width: 2rem; height: 2rem; display: grid; place-items: center; padding: 0; border: 1px solid var(--line-hot);
  border-radius: 5px; background: rgba(255,255,255,.025); transition: color 130ms, border-color 130ms, background 130ms, transform 130ms;
}
button.uih-x:hover { transform: rotate(4deg); border-color: var(--red); box-shadow: 0 0 15px rgba(255,77,77,.08); }
.uih-top { padding: .7rem .82rem .62rem; gap: .55rem; background: linear-gradient(180deg, rgba(5,13,9,.55), rgba(0,0,0,.22)); }
.uih-scroll, .uih-footblock, .uih-sell, .uih-chips, .uih-ac {
  scrollbar-width: thin; scrollbar-color: rgba(77,242,162,.38) rgba(0,0,0,.18);
}
.uih-scroll::-webkit-scrollbar, .uih-footblock::-webkit-scrollbar, .uih-sell::-webkit-scrollbar, .uih-chips::-webkit-scrollbar, .uih-ac::-webkit-scrollbar { width: 9px; height: 9px; }
.uih-scroll::-webkit-scrollbar-track, .uih-footblock::-webkit-scrollbar-track, .uih-sell::-webkit-scrollbar-track, .uih-chips::-webkit-scrollbar-track, .uih-ac::-webkit-scrollbar-track { background: rgba(0,0,0,.16); }
.uih-scroll::-webkit-scrollbar-thumb, .uih-footblock::-webkit-scrollbar-thumb, .uih-sell::-webkit-scrollbar-thumb, .uih-chips::-webkit-scrollbar-thumb, .uih-ac::-webkit-scrollbar-thumb { background: rgba(77,242,162,.3); border: 2px solid transparent; border-radius: 8px; background-clip: padding-box; }
.uih-scroll::-webkit-scrollbar-thumb:hover, .uih-footblock::-webkit-scrollbar-thumb:hover, .uih-sell::-webkit-scrollbar-thumb:hover { background-color: rgba(77,242,162,.55); }
.uih-wallet { border-color: rgba(100,230,168,.24); border-radius: 6px; background: linear-gradient(90deg, rgba(77,242,162,.075), rgba(77,242,162,.025)); box-shadow: inset 0 1px rgba(255,255,255,.025); }
.uih-search { border-radius: 6px; transition: border-color 140ms, box-shadow 140ms, background 140ms; }
.uih-search:focus-within { background: rgba(2,15,9,.6); box-shadow: 0 0 0 3px rgba(77,242,162,.09); }
.uih-chip, .uih-page-btn, .uih-btn, .uih-mini, .uih-step, button.uih-race, button.uih-lookchip {
  transition: color 130ms, border-color 130ms, background 130ms, box-shadow 130ms, transform 130ms, filter 130ms;
}
.uih-chip:hover, .uih-page-btn:hover:not([disabled]), .uih-btn:hover:not(:disabled), .uih-mini:hover, .uih-step:hover, button.uih-race:hover, button.uih-lookchip:hover { transform: translateY(-1px); }
.uih-chip.on, .uih-btn.buy, .uih-mini, .uih-step.on, button.uih-race.on { box-shadow: 0 5px 16px rgba(77,242,162,.14), inset 0 1px rgba(255,255,255,.18); }
.uih-btn.sell { box-shadow: 0 5px 16px rgba(255,194,75,.12), inset 0 1px rgba(255,255,255,.18); }
.uih-btn:focus-visible, .uih-chip:focus-visible, .uih-page-btn:focus-visible, .uih-mini:focus-visible,
.uih-step:focus-visible, .uih-item-hd:focus-visible, .uih-mail-rowhd:focus-visible, .uih-help-hd:focus-visible {
  outline: 2px solid var(--green); outline-offset: 2px;
}
.uih-table { border-color: rgba(104,225,166,.21); border-radius: 6px; background: rgba(0,0,0,.12); }
.uih-tr { transition: background 120ms, box-shadow 120ms; }
.uih-tr.head { background: rgba(7,18,13,.985); box-shadow: 0 5px 12px rgba(0,0,0,.25); }
.uih-tr:not(.head):nth-child(odd) { background: rgba(255,255,255,.012); }
.uih-tr:not(.head):hover { background: linear-gradient(90deg, rgba(77,242,162,.075), rgba(77,242,162,.018)); box-shadow: inset 2px 0 var(--green); }
.uih-item, .uih-mail-row, .uih-inv-eqrow, .uih-score-stat, .uih-skill-card, .uih-quest-row {
  border-color: rgba(113,204,160,.16); border-radius: 6px;
  background: linear-gradient(145deg, rgba(15,25,21,.88), rgba(5,10,8,.88));
  box-shadow: 0 5px 15px rgba(0,0,0,.13); transition: border-color 140ms, background 140ms, transform 140ms, box-shadow 140ms;
}
.uih-item:hover, .uih-mail-row:hover, .uih-inv-eqrow:hover, .uih-score-stat:hover, .uih-skill-card:hover, .uih-quest-row:hover {
  border-color: rgba(91,223,157,.34); box-shadow: 0 8px 22px rgba(0,0,0,.22);
}
.uih-score-hero { position: relative; overflow: hidden; border-radius: 8px; background: radial-gradient(120% 160% at 0 0, rgba(77,242,162,.13), transparent 65%), rgba(5,12,9,.9); }
.uih-score-hero::after { content: 'COMBAT PROFILE'; position: absolute; right: .65rem; top: .55rem; color: rgba(77,242,162,.16); font-size: .56em; letter-spacing: .16em; }
.uih-sec, .uih-inv-h { position: relative; padding-left: .65rem; }
.uih-sec::before, .uih-inv-h::before { content: ''; position: absolute; left: 0; top: .05rem; bottom: .08rem; width: 2px; background: currentColor; box-shadow: 0 0 7px currentColor; }
.uih-sell { border-radius: 6px; background: linear-gradient(155deg, rgba(255,194,75,.055), rgba(8,7,3,.7)); }
.uih-footblock { background: linear-gradient(180deg, rgba(0,0,0,.18), rgba(4,9,7,.92)); box-shadow: 0 -9px 24px rgba(0,0,0,.16); }
.uih-toast { border-top-color: rgba(255,194,75,.35); box-shadow: 0 -8px 25px rgba(0,0,0,.25), inset 3px 0 var(--amber); animation: uih-toast-in 160ms ease both; }
.uih-empty { min-height: 5rem; display: grid; place-items: center; text-align: center; padding: 1rem; border: 1px dashed rgba(109,215,163,.18); border-radius: 6px; }
.uih-cmp, .uih-namemenu, .uih-ac { border-radius: 7px; border-color: rgba(93,229,162,.38); background: linear-gradient(145deg, rgba(15,27,22,.99), rgba(4,9,7,.99)); box-shadow: 0 14px 40px rgba(0,0,0,.68), 0 0 25px rgba(77,242,162,.05); }
.uih-split { transition: background 140ms, border-color 140ms; }
.uih-split:hover { background: rgba(77,242,162,.045); border-color: rgba(77,242,162,.34); }

@keyframes uih-open { from { opacity: 0; transform: translateY(7px) scale(.993); } to { opacity: 1; transform: none; } }
@keyframes uih-toast-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

@media (max-width: 720px) {
  .uih-channel { display: none; }
  .uih-head { min-height: 2.9rem; }
  .uih-top { padding-inline: .58rem; }
  .uih-scroll { padding-inline: .5rem; }
  .uih-footblock { padding-inline: .55rem; }
  .uih-btn, .uih-chip, .uih-page-btn { min-height: 2rem; }
}
@media (prefers-reduced-motion: reduce) {
  .uih-pop, .uih-toast { animation: none !important; }
  .uih-chip, .uih-page-btn, .uih-btn, .uih-mini, .uih-step, button.uih-race,
  button.uih-lookchip, .uih-item, .uih-mail-row, .uih-inv-eqrow, .uih-score-stat,
  .uih-skill-card, .uih-quest-row { transition-duration: .01ms !important; }
}

/* ============================================================================
   PRE-GAME EXPERIENCE — cinematic Capsule Corp login, roster, and bio-forge.
   This layer deliberately remains scoped to the login overlay so none of the
   richer lighting or geometry leaks into the dense in-game cockpit.
   ========================================================================= */
.play-view[data-plg='pregame'] .plg-overlay {
  isolation: isolate;
  background:
    radial-gradient(circle at 50% 115%, rgba(44, 190, 128, 0.12), transparent 38%),
    linear-gradient(145deg, #040807 0%, #07100e 46%, #020504 100%);
  overflow-x: hidden;
}
.plg-atmos { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.plg-stars { position: absolute; inset: 0; }
.plg-stars i {
  position: absolute; left: var(--x); top: var(--y); width: var(--s); height: var(--s);
  border-radius: 50%; background: #dcfff0; opacity: var(--o);
  box-shadow: 0 0 3px rgba(143, 255, 205, 0.48);
  animation: plg-twinkle var(--d) ease-in-out infinite alternate;
}
.plg-nebula {
  position: absolute; inset: -8%; opacity: 0.5;
  background: radial-gradient(ellipse at 44% 55%, rgba(10,112,76,.2), transparent 52%), radial-gradient(ellipse at 70% 32%, rgba(245,178,52,.07), transparent 38%);
}
.plg-planet {
  position: absolute; right: -13rem; bottom: -20rem; width: 44rem; aspect-ratio: 1;
  border-radius: 50%; opacity: 0.5;
  background: radial-gradient(circle at 34% 28%, #406b56 0, #14281f 21%, #07110d 56%, #010302 72%);
  box-shadow: -25px -20px 90px rgba(69, 233, 155, 0.11), inset 32px 22px 80px rgba(146, 255, 205, 0.09);
}
.plg-planet-glow { position: absolute; inset: -4%; border-radius: inherit; border: 1px solid rgba(107, 255, 185, 0.13); }
.plg-planet-grid {
  position: absolute; inset: 9%; border-radius: inherit; opacity: 0.16;
  background: repeating-linear-gradient(0deg, transparent 0 24px, rgba(100,255,181,.18) 25px 26px), repeating-linear-gradient(90deg, transparent 0 30px, rgba(100,255,181,.13) 31px 32px);
  mask-image: radial-gradient(circle, #000 35%, transparent 72%);
}
.plg-orbit { position: absolute; right: -14rem; bottom: -5rem; width: 55rem; height: 17rem; border: 1px solid rgba(101, 245, 176, 0.1); border-radius: 50%; transform: rotate(-16deg); }
.plg-orbit.o2 { right: -20rem; bottom: 5rem; width: 64rem; transform: rotate(-25deg); opacity: .6; }

.plg-stage { position: relative; z-index: 1; animation: plg-arrive 420ms cubic-bezier(.2,.75,.25,1) both; }
.plg-stage-in { position: relative; }
.plg-brand { filter: drop-shadow(0 14px 28px rgba(0,0,0,.65)); }
.plg-logo { letter-spacing: .035em; text-shadow: 0 2px 0 #070b09, 0 0 16px rgba(255,192,52,.2), 0 0 34px rgba(74,229,153,.12); }
.plg-star { display: inline-block; color: #f3bd3e; filter: drop-shadow(0 0 7px rgba(255,188,45,.65)); animation: plg-star-pulse 2.7s ease-in-out infinite; }
.plg-sub { letter-spacing: .42em; opacity: .82; }

.plg-card {
  position: relative; overflow: hidden;
  border-color: rgba(121, 240, 182, 0.28);
  background: linear-gradient(145deg, rgba(16, 28, 23, .94), rgba(5, 11, 9, .96));
  box-shadow: 0 22px 80px rgba(0,0,0,.58), 0 0 0 1px rgba(130,255,194,.04) inset, 0 0 44px rgba(41,193,123,.055);
  backdrop-filter: blur(18px) saturate(1.15);
}
.plg-card::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 28%, rgba(159,255,209,.035) 45%, transparent 62%);
  transform: translateX(-90%); animation: plg-scan 9s ease-in-out infinite;
}
.plg-card > * { position: relative; z-index: 1; }
.plg-card-meta {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  margin: -2px -2px 22px; padding: 8px 11px;
  border-bottom: 1px solid rgba(112, 235, 175, .16);
  color: var(--fg-faint); font-family: var(--mono); font-size: .56rem; font-weight: 700; letter-spacing: .13em;
  background: rgba(101, 233, 169, .035);
}
.plg-meta-live { display: flex; align-items: center; gap: 7px; color: #74e9aa; }
.plg-meta-live::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; box-shadow: 0 0 8px currentColor; }
.plg-h { text-wrap: balance; }
.plg-inp {
  background: rgba(0,0,0,.32); border-color: rgba(118,219,170,.22);
  box-shadow: inset 0 1px 9px rgba(0,0,0,.3); transition: border-color 160ms, box-shadow 160ms, background 160ms;
}
.plg-inp:hover { border-color: rgba(118,219,170,.38); }
.plg-inp:focus { background: rgba(1,11,7,.55); border-color: #62d99d; box-shadow: 0 0 0 3px rgba(77,215,147,.11), inset 0 1px 9px rgba(0,0,0,.28); }
.plg-btn { position: relative; overflow: hidden; transform: translateZ(0); transition: transform 150ms, border-color 150ms, color 150ms, box-shadow 150ms, background 150ms; }
.plg-btn::after { content: ''; position: absolute; inset: 0; background: linear-gradient(100deg, transparent 15%, rgba(255,255,255,.14), transparent 85%); transform: translateX(-120%); transition: transform 380ms; }
.plg-btn:hover::after { transform: translateX(120%); }
.plg-btn:hover:not(:disabled) { transform: translateY(-2px); }
.plg-btn.primary { box-shadow: 0 7px 22px rgba(54,211,137,.14), inset 0 1px rgba(255,255,255,.14); }

.plg-grid { perspective: 1100px; }
.plg-charcard {
  isolation: isolate; overflow: hidden; contain: layout paint style; border-color: color-mix(in srgb, var(--char) 32%, var(--line));
  background: linear-gradient(150deg, color-mix(in srgb, var(--char) 7%, #101713), rgba(4,9,7,.94));
  box-shadow: 0 12px 25px rgba(0,0,0,.25); transition: transform 180ms, border-color 180ms, box-shadow 180ms;
}
.plg-charcard:hover, .plg-charcard:focus-visible { transform: translateY(-4px); border-color: color-mix(in srgb, var(--char) 68%, #fff 8%); box-shadow: 0 18px 38px rgba(0,0,0,.42), 0 0 24px color-mix(in srgb, var(--char) 12%, transparent); }
.plg-card-aura { position: absolute; z-index: -1; width: 10rem; height: 10rem; left: -4rem; top: -5rem; border-radius: 50%; background: radial-gradient(circle, var(--char), transparent 68%); opacity: .095; }
.plg-overlay[data-view='select'] .plg-card { backdrop-filter: none; }
.plg-overlay[data-view='select'] .plg-stars i { animation: none; opacity: calc(var(--o) * .72); }
.plg-emblem { border-radius: 50%; border-color: color-mix(in srgb, var(--char) 45%, var(--line)); box-shadow: 0 0 18px color-mix(in srgb, var(--char) 12%, transparent), inset 0 0 14px rgba(0,0,0,.5); }
.plg-sel-t, .plg-cc-h { text-shadow: 0 0 20px rgba(82,229,155,.12); }

.plg-cc-card { overflow: visible; }
.plg-cc-step { position: relative; border-color: rgba(115,224,173,.18); background: linear-gradient(135deg, rgba(17,30,24,.74), rgba(5,10,8,.68)); box-shadow: 0 10px 30px rgba(0,0,0,.18); }
.plg-step-n { box-shadow: 0 0 18px rgba(74,222,151,.14); }
.plg-racecard { transition: transform 160ms, border-color 160ms, box-shadow 160ms, background 160ms; }
.plg-racecard:hover { transform: translateY(-3px); }
.plg-racecard.on { box-shadow: 0 0 0 1px var(--rc), 0 12px 24px rgba(0,0,0,.24), inset 0 0 1.4em -0.6em var(--rc); }
.plg-scouter { box-shadow: 0 20px 45px rgba(0,0,0,.35), 0 0 28px rgba(72,218,149,.06); }
.plg-scouter::after { content: 'SCOUTER FEED'; position: absolute; right: 10px; bottom: 7px; color: rgba(114,228,174,.22); font: 700 .48rem/1 var(--mono); letter-spacing: .16em; }

.plg-warp { position: relative; width: 130px; height: 130px; margin: 2px auto -14px; }
.plg-warp-ring, .plg-warp-core { position: absolute; inset: 18%; border-radius: 50%; border: 1px solid rgba(102,245,176,.5); }
.plg-warp-ring.r1 { animation: plg-warp 1.5s ease-out infinite; }
.plg-warp-ring.r2 { animation: plg-warp 1.5s .72s ease-out infinite; }
.plg-warp-core { inset: 39%; border: 0; background: #6cf0af; box-shadow: 0 0 12px #6cf0af, 0 0 38px rgba(108,240,175,.5); animation: plg-core 900ms ease-in-out infinite alternate; }
.plg-entering { position: relative; z-index: 2; justify-content: center; }

@keyframes plg-arrive { from { opacity: 0; transform: translateY(12px) scale(.992); } to { opacity: 1; transform: none; } }
@keyframes plg-twinkle { from { opacity: calc(var(--o) * .45); transform: scale(.7); } to { opacity: var(--o); transform: scale(1.15); } }
@keyframes plg-drift { to { transform: translate3d(5%, -3%, 0) rotate(8deg); } }
@keyframes plg-star-pulse { 50% { transform: scale(1.16) rotate(9deg); filter: drop-shadow(0 0 12px rgba(255,188,45,.9)); } }
@keyframes plg-scan { 0%, 72% { transform: translateX(-95%); } 92%, 100% { transform: translateX(95%); } }
@keyframes plg-warp { from { opacity: .85; transform: scale(.3); } to { opacity: 0; transform: scale(1.35); } }
@keyframes plg-core { to { transform: scale(1.18); } }

@media (max-width: 900px) {
  .plg-planet { right: -20rem; opacity: .35; }
  .plg-card-meta { margin-bottom: 17px; }
  .plg-cc-card { overflow: hidden; }
}
@media (max-width: 600px) {
  .plg-stage { padding-inline: 10px; }
  .plg-card { backdrop-filter: blur(10px); }
  .plg-card-meta { font-size: .48rem; gap: 8px; letter-spacing: .08em; }
  .plg-sub { letter-spacing: .25em; }
  .plg-charcard:hover, .plg-charcard:focus-visible, .plg-btn:hover:not(:disabled), .plg-racecard:hover { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .plg-stage, .plg-stars i, .plg-nebula, .plg-star, .plg-card::after,
  .plg-warp-ring, .plg-warp-core { animation: none !important; }
  .plg-btn, .plg-charcard, .plg-racecard, .plg-inp { transition-duration: .01ms !important; }
  .plg-stars i { opacity: calc(var(--o) * .65); }
  .plg-btn::after { display: none; }
}

/* ---- visual restraint pass ------------------------------------------------
   Keep the site's original terminal/scouter character: flatter working
   surfaces, limited glow, and no decorative lift on routine controls. */
.uih-pop {
  background: #07100d;
  box-shadow: 0 18px 55px rgba(0,0,0,.72), inset 0 1px rgba(170,255,215,.035);
}
.uih-head { background: #0d1b15; box-shadow: none; }
.uih-item, .uih-mail-row, .uih-inv-eqrow, .uih-score-stat, .uih-skill-card, .uih-quest-row {
  background: var(--bg-well); box-shadow: none;
}
.uih-item:hover, .uih-mail-row:hover, .uih-inv-eqrow:hover, .uih-score-stat:hover,
.uih-skill-card:hover, .uih-quest-row:hover { box-shadow: none; }
.uih-chip:hover, .uih-page-btn:hover:not([disabled]), .uih-btn:hover:not(:disabled),
.uih-mini:hover, .uih-step:hover, button.uih-race:hover, button.uih-lookchip:hover { transform: none; }
