/* DOM parts of the game screen (src/client/play/ui.ts): the player menu,
   invitations, the trade house window and the panel's 行会 / 商城 / 充值 pages, in the HUD's colours.
   Typefaces and sizes: the same three faces and one scale as the canvas (src/shared/fonts.ts):
   --gm-kai 题字 (page titles, the death screen), --gm-hei 正文 (everything read), --gm-num 跳字.
   Each language its own 题字 and 正文 (用户 2026-10-14, docs/ui-rules.md「字体规划」): the :root:lang(...) lines below, the
   same families as src/shared/fonts.ts FACES; the stylesheets of English and Japanese faces are added by lang.ts. */
@import url(/play/fonts/kai/kai.css);
@font-face { font-family: DmgDigits; src: url(/play/fonts/cinzel-digits.woff2) format("woff2"); font-weight: 700; }
:root { --gm-hei: "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif; --gm-kai: Kai, "Kaiti SC", "STKaiti", "KaiTi", serif;
  --gm-num: DmgDigits, var(--gm-hei); --gm-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Colours (docs/colors.md): the same names and values as src/shared/colors.ts COLOR; tools/colors-test.ts checks they match. */
  --gm-c-main: #f2ead8; --gm-c-sub: #c9bfa9; --gm-c-dim: #9a8f7c; --gm-c-off: #5c5446; --gm-c-gold: #e6c88a; --gm-c-bright: #f0d69a;
  --gm-c-good: #8fd18a; --gm-c-bad: #e0584a; --gm-c-hp: #ff8a7a; --gm-c-mp: #9ab8ff; --gm-c-ink: #1a1612; --gm-c-coin: #8fd0ff; --gm-c-blood: #b3271d;
  /* Surfaces, lines, corners, control heights (docs/ui-rules.md「面、线与按钮」): the same as src/shared/colors.ts SURFACE, LINE,
     RADIUS, CONTROL_H; tools/colors-test.ts checks they match and that nothing else is written. Hover lifts one step. */
  --gm-s-sunk: #0c0a08; --gm-s-card: #15120e; --gm-s-raise: #2a251f; --gm-s-hover: #3a332a; --gm-s-gold: #3a2f1c; --gm-s-gold-hover: #4a3c24;
  --gm-s-bad: #3a201b; --gm-s-bad-hover: #4a2822; --gm-s-coin: #16293a; --gm-s-float: rgba(18, 15, 12, .95); --gm-s-sheet: #120f0c;
  --gm-l-line: #3b3428; --gm-l-faint: #221c15; --gm-l-edge: #6b5a3e; --gm-l-gold: #e6c88a;
  --gm-r-s: 3px; --gm-r-m: 6px; --gm-r-full: 999px; --gm-h-lg: 32px; --gm-h-md: 28px; --gm-h-sm: 22px; --gm-h-touch: 40px; --gm-row-h: 32px; --gm-h-head: 46px; --gm-sel-w: 2px; --gm-w-num: 104px; --gm-off: .45; --gm-grey: .6; --gm-gap: .2; /* --gm-row-h: a list's row on a computer (a phone's is --gm-h-touch), src/shared/space.ts ROW_H; --gm-h-head: a page's head to its line, every page and sheet (PAGE_HEAD); --gm-sel-w: the 线·金 a selected row wears down its left, a selected cell round it (colors.ts SEL_W); --gm-w-num: every number box, NUM_BOX_W; --gm-off: whatever cannot be used now, faint (the only see-through, DISABLED_ALPHA); --gm-grey: a picture greyed out, src/shared/colors.ts GREY_BRIGHT; --gm-gap: the faint sweep of 施法间隔 on a skill key, GAP_ALPHA */
  /* --gm-track-title: a Chinese page name's letter spacing (楷书 20; the only spacing in the game, docs/ui-rules.md「字体规划」):
     src/shared/fonts.ts TITLE_TRACK, the canvas draws FS.title × it; --gm-strip: the fight left showing beside a phone sheet
     (src/shared/space.ts PHONE_STRIP_W, the chat sheet on the canvas the same); --gm-equip-w: the gear's width, the 角色 page's
     left column and 查看装备's narrow sheet (src/client/hud/equip-layout.ts EQUIP_W, the original art's layout: 材质) */
  --gm-track-title: .14em; --gm-strip: 170px; --gm-equip-w: 236px;
  /* the fight left showing beside a phone sheet (菜单、NPC 单子、聊天、技能、绑定和通行证卡): 面·凹 at 30%, one value (docs/ui-rules.md「面板统一版式」;
     hud.ts SCRIM_ALPHA for the chat sheet drawn on the canvas) */
  --gm-scrim: color-mix(in srgb, var(--gm-s-sunk) 30%, transparent);
  --gm-scrim-deep: color-mix(in srgb, var(--gm-s-sunk) 62%, transparent); /* the gestures' picture (touch.ts gestureCard): the fight under it a step darker than a sheet's */
  /* A phone sheet's tab column (菜单、聊天; 用户 2026-10-15 批准草图 https://claude.ai/artifact/RLXFZwuFm3LEPwpiJmwgVv ): as wide as
     this language's longest tab word needs at 17, 56 at least, at most what the screen gives (667 wide 75, 88 at most);
     the words shrink (to 13 at least) only when the longest does not fit that (src/shared/space.ts PHONE_TABCOL,
     phoneTabs). phone-menu.ts fitTabs puts both here; until it has, 56 and 17. */
  --gm-tabcol: 56px; --gm-tabfont: 17px;
  /* The type scale (docs/ui-rules.md「字体规划」): src/shared/fonts.ts FS, step by step (foot 11, label 12, body 13, emph 15, phone 17,
     title 20). Every font size in the game's pages is one of these (or 72, or a 字号例外 on its line); tools/type-test.ts checks. */
  --gm-fs-foot: 11px; --gm-fs-label: 12px; --gm-fs-body: 13px; --gm-fs-emph: 15px; --gm-fs-phone: 17px; --gm-fs-title: 20px;
  /* The phone's one item cell (用户 2026-10-15 批准草图 https://claude.ai/artifact/E3C1KoH7BUuVUiFEBZekGJ 方案 B): phone-menu.ts
     measure() sets it from the NPC sheet, 22 to 40 (phone-cell.ts phoneCell); 40 until it has. */
  --gm-cell: 40px;
  /* A card in a row of cards (商城的外观卡片): never narrower than this; a row holds as many as fit at it, at most three (宝宝 two),
     so 送人 and 购买 keep their words on the smallest phone (用户 2026-10-15 批准草图 https://claude.ai/artifact/RJMbd8hWLcvGNV7GNbr9yV 第 1 条). */
  --gm-card-min: 120px;
  /* The 角色 page's numbers on a phone (.gm-pm-stats b): 13, or one size down to 12 when the widest of them does not
     fit its column — all of them together (src/shared/space.ts PHONE_STATFONT, phoneStatFont; phone-menu.ts fitStats sets it
     on the grid; 同一草图第 2 条). */
  --gm-statfont: 13px;
  --gm-tint-nomp: #3a4cff; /* a skill you lack the MP for: src/shared/colors.ts TINT.noMp, multiplied over its picture */
  /* A phone's edges, shadows and glows (docs/ui-rules.md「面、线与按钮」, 用户 2026-10-13 同意草图
     https://claude.ai/artifact/9CKRWXhCoC7apBgvNVZgfF 「档位」): only these; tools/phone-test.ts checks. */
  --gm-sh-key: 0 0 0 1.5px var(--gm-s-sunk), 0 2px 5px color-mix(in srgb, var(--gm-s-sunk) 80%, transparent); /* a key on its round ground */
  --gm-sh-icon: drop-shadow(0 0 1px var(--gm-s-sunk)) drop-shadow(0 1px 2px var(--gm-s-sunk)); /* a picture with no ground */
  --gm-sh-badge: 0 0 0 1px var(--gm-s-sunk); /* a small badge on a key's corner (宝宝's moon, a mate's job, the − that takes a key off) */
  --gm-sh-text: -1px -1px 0 var(--gm-s-sunk), 1px -1px 0 var(--gm-s-sunk), -1px 1px 0 var(--gm-s-sunk), 1px 1px 0 var(--gm-s-sunk), 0 1px 2px var(--gm-s-sunk); /* words on the fight */
  --gm-glow-on: 0 0 0 2px var(--gm-l-gold), 0 0 8px var(--gm-l-gold); /* on, in use, locked: a round key */
  --gm-glow-icon: drop-shadow(0 0 6px var(--gm-l-gold)); /* on, in use, locked: a picture with no ground */
  --gm-sh-lift: 0 0 0 1.5px var(--gm-s-sunk), 0 6px 14px color-mix(in srgb, var(--gm-s-sunk) 90%, transparent); /* a key lifted (scale 1.14): the one a dragged skill will land on; no gold ring, it would join the bronze rim's 三段环 */
  --gm-sh-ready: 0 0 0 1.5px var(--gm-s-sunk), 0 4px 9px color-mix(in srgb, var(--gm-s-sunk) 85%, transparent); /* the keys a little lifted (scale 1.06) while an active skill is picked in the 技能 sheet: a tap puts it there */
  --gm-glow-can: 0 0 0 1.5px var(--gm-s-sunk), 0 0 0 4px color-mix(in srgb, var(--gm-c-gold) 40%, transparent); /* the keys a dragged skill can land on: faint */
  /* Over everything else on a computer (菜单, 问答框, 邀请卡, 技能提示): one shadow for the floating layers, its colour 面·凹 (docs/ui-rules.md「面、线与按钮」);
     a phone's floating boxes (数字盘, 打字条, 邀请卡) have none, their edge only, as the sheets. */
  --gm-sh-float: 0 14px 40px color-mix(in srgb, var(--gm-s-sunk) 60%, transparent);
  /* A phone's 三段环 on a skill's own bronze rim (熟练度 0–3 and its training, docs/hud.md「手机」; 用户 2026-10-15：「用里层那个圈显示」,
     草图 https://claude.ai/artifact/TeFiSrk9imH7i55kFu2kwB 的 C): the levels not reached under a 面·凹 veil, the notches 面·凹, a level
     up's light 字·主 (touch.ts ringOf). */
  --gm-ring-veil: color-mix(in srgb, var(--gm-s-sunk) 65%, transparent); --gm-ring-cut: color-mix(in srgb, var(--gm-s-sunk) 90%, transparent);
  --gm-ring-light: color-mix(in srgb, var(--gm-c-main) 70%, transparent);
  /* The bronze rim's inner edge on a skill's round picture, in from the key's edge, of its size: (256 − 232) / 512
     (tools/convert/own-art/gen/lib.py RIM_IN 232, of a 512 picture). The cooling sweep and the deep blue of 蓝不够 stay inside
     it, so the 三段环 on the rim is never darkened (用户 2026-10-15, docs/ui-rules.md「格子的意图语言」); tools/phone-test.ts checks. */
  --gm-rim-in: 4.7%;
  /* Spacing (docs/ui-rules.md「间距」): the same as src/shared/space.ts SPACE; gaps, paddings and margins are only these seven numbers
     (a control's own inside — buttons, chips, tabs — and art that says 「材质」 aside). tools/space-test.ts checks. */
  --gm-sp-hair: 2px; --gm-sp-tight: 4px; --gm-sp-near: 6px; --gm-sp-row: 8px; --gm-sp-inner: 10px; --gm-sp-section: 14px; --gm-sp-page: 18px;
  /* Motion (docs/ui-rules.md「动效」): the same as src/shared/motion.ts MOTION; in fast and settling, out slow then fast. */
  --gm-t-quick: 120ms; --gm-t-mid: 300ms; --gm-t-slow: 500ms; --gm-t-hold: 1200ms; --gm-t-blink: 1000ms; --gm-t-breathe: 1400ms; --gm-t-copied: 2000ms;
  --gm-ease-out: cubic-bezier(.33, 1, .68, 1); --gm-ease-in: cubic-bezier(.32, 0, .67, 0);
  /* What covers what (docs/ui-rules.md「叠放顺序和鼠标」), over the game canvas, bottom to top: the Alt+X countdown, the DOM pages,
     the update line, the see-through canvas with the item in hand and the tooltips (.gm-top), menus and invitations, the
     confirmation card, the lost-connection box. #ui is not a layer of its own, so its parts can sit on both sides of .gm-top.
     tools/space-test.ts checks z-index reads these. */
  --gm-z-countdown: 3; --gm-z-ui: 5; --gm-z-update: 6; --gm-z-top: 7; --gm-z-pop: 8; --gm-z-ask: 50; --gm-z-alarm: 60; }
:root:lang(zh-TW) { --gm-hei: "PingFang TC", "Microsoft JhengHei", "Noto Sans TC", "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif; }
:root:lang(en) { --gm-kai: Cinzel, Kai, "Kaiti SC", "STKaiti", "KaiTi", serif; --gm-hei: NotoSans, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif; }
:root:lang(ja) { --gm-kai: Klee, Kai, "Kaiti SC", "STKaiti", "KaiTi", serif; --gm-hei: NotoSansJP, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", sans-serif; }
/* One scrollbar everywhere (docs/ui-rules.md「面、线与按钮」): thin, 线·边 on nothing, the 商城's; never the browser's white bar.
   No specificity (*), so a box that hides its bar (its own width none) still does. */
* { scrollbar-width: thin; scrollbar-color: var(--gm-l-edge) transparent; }
/* The sizes we write are the sizes drawn (docs/ui-rules.md「字体规划」·手机): an iPhone held sideways blows a block's text up
   on its own (a 12 说明 drawn about 17, 用户 2026-10-17) unless told not to. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
#ui { position: fixed; inset: 0; pointer-events: none; font: var(--gm-fs-body) var(--gm-hei); color: var(--gm-c-main); }
#ui > * { pointer-events: auto; }
/* 换行 (docs/ui-rules.md, 用户 2026-10-16 草图 D2): a label or a row's words that do not fit fold into even lines, never one
   字 alone on the last (「ギルドと称号を常に表 / 示」); 日文 folds at its phrases, not inside a word (「称 / 号」「スキ / ル」).
   Inherited, so every page, box and sheet; a long text (over the browser's few lines) wraps as before. */
#ui, #lobby { text-wrap: balance; }
:root:lang(ja) :is(#ui, #lobby) { word-break: auto-phrase; }
:is(#ui, #lobby) textarea { text-wrap: wrap; } /* what one is typing wraps as typed, not evened out under the caret */
:where(#ui) button { font: inherit; } /* no weight: a button with its own size or face (.gm-chip, .gm-btn.sm, the 充值 amount) keeps it */
/* What an empty box says (占位符: 「全部」 in the storage's 取出, 写满了 / 禁言 on the board, the chat's 「说点什么」): 字·弱 in every
   box and text box of the game, no weight (用户 2026-10-16, docs/ui-rules.md「输入框和键盘」) */
:where(input, textarea)::placeholder { color: var(--gm-c-dim); }
:where(#ui) small { font-size: var(--gm-fs-foot); } /* 注脚 (no weight: a class sets its own size over it); the browser's own 「smaller」 is 10.8, off the ladder (docs/ui-rules.md「字体规划」) */
.gm-menu { position: fixed; z-index: var(--gm-z-pop); min-width: 168px; padding: var(--gm-sp-near); border-radius: var(--gm-r-m); background: var(--gm-s-float); border: 1px solid var(--gm-l-edge);
  box-shadow: var(--gm-sh-float); }
.gm-menu-head { display: flex; flex-direction: column; padding: var(--gm-sp-near) var(--gm-sp-inner) var(--gm-sp-near); }
.gm-menu-head b { color: var(--gm-c-main); font-size: var(--gm-fs-emph); font-weight: 600; }
.gm-menu-head span { color: var(--gm-c-sub); font-size: var(--gm-fs-label); } /* the guild under a name is 字·次, as over heads (docs/colors.md) */
.gm-sep { height: 1px; margin: var(--gm-sp-tight) var(--gm-sp-near); background: var(--gm-l-line); }
.gm-item { box-sizing: border-box; display: flex; align-items: center; width: 100%; min-height: var(--gm-row-h); text-align: left; padding: var(--gm-sp-tight) var(--gm-sp-inner); border: 0; border-radius: var(--gm-r-s); background: none; color: var(--gm-c-main); cursor: pointer;
  transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-item:hover { background: var(--gm-s-raise); } }
.gm-item.danger { color: var(--gm-c-bad); }
.gm-item small { margin-left: var(--gm-sp-tight); color: var(--gm-c-dim); }
#gm-invites { position: fixed; z-index: var(--gm-z-pop); left: 16px; top: 16px; transform-origin: 0 0; display: flex; flex-direction: column; gap: var(--gm-sp-near); }
#gm-invites.gm-after-party:not(:empty) { margin-top: var(--gm-sp-near); padding-top: var(--gm-sp-section); border-top: 1px solid var(--gm-l-line); }
/* a computer's own way out of the party (party-chip.ts, docs/hud.md「布局」): 按钮 小 under the teammates' cards, the HUD's scale */
.gm-party-chip { position: fixed; z-index: var(--gm-z-pop); left: 16px; top: 16px; transform-origin: 0 0; pointer-events: auto; }
.gm-party-chip[hidden] { display: none; }
/* An invitation: as big as a party card (170 × 38), top left under them (用户决定，2026-09-30). */
.gm-inv { position: relative; overflow: hidden; box-sizing: border-box; width: 170px; min-height: 38px; display: flex; align-items: center; gap: var(--gm-sp-tight); padding: var(--gm-sp-tight) var(--gm-sp-tight) var(--gm-sp-tight) var(--gm-sp-row);
  border-radius: var(--gm-r-m); background: var(--gm-s-float); border: 1px solid var(--gm-l-gold); box-shadow: var(--gm-sh-float); font-size: var(--gm-fs-label); }
.gm-inv-text { flex: 1; min-width: 0; line-height: 1.4; overflow-wrap: anywhere; } /* a long guild name wraps to a second line, nothing is cut off (规范检查 2026-10-07: was … with the browser title) */
.gm-inv-text b { color: var(--gm-c-gold); font-weight: 500; margin-right: var(--gm-sp-tight); }
.gm-inv button { width: 24px; height: var(--gm-h-sm); padding: 0; border: 0; border-radius: var(--gm-r-full); cursor: pointer; font-size: var(--gm-fs-label); line-height: var(--gm-h-sm); }
.gm-inv-yes { background: var(--gm-s-gold); color: var(--gm-c-gold); transition: background var(--gm-t-quick) var(--gm-ease-out); }
.gm-inv-no { background: var(--gm-s-raise); color: var(--gm-c-bad); transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-inv-yes:hover { background: var(--gm-s-gold-hover); } }
@media (hover: hover) { .gm-inv-no:hover { background: var(--gm-s-hover); color: var(--gm-c-bad); } } /* ✕ = 拒绝: 坏, like 行会页's 拒绝 (colors.md) */
.gm-inv i { position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; background: var(--gm-l-gold); animation: gm-shrink linear forwards; }
/* The gold you offer in a trade: a text box over the HUD's gold box (src/client/hud/hud.ts buildTrade). */
/* A gold box of a canvas window: the canvas draws its edge (red when filled wrong, with why under it: HudState.goldSay). */
.gm-trade-gold { position: fixed; transform-origin: 0 0; box-sizing: border-box; padding: 0; border: 0; background: transparent; color: var(--gm-c-gold); font: var(--gm-fs-body) var(--gm-hei); outline: none; }
/* Every 弹窗 (src/client/play/ui.ts dialog, docs/ui-rules.md「弹窗」, 用户定 2026-10-09): the panel pages' head, the body, the
   buttons on the last row at the right. Gold edge like the invite cards: gold = waiting for your answer (用户选 B，
   2026-10-05, https://claude.ai/artifact/PPcAoKHGHQAyrFUNQ4X5M7). In the middle by default (questions). */
.gm-dlg { position: fixed; left: 50%; top: 38%; transform: translate(-50%, -50%); box-sizing: border-box; width: 340px; padding: 0 var(--gm-sp-page) var(--gm-sp-page); border-radius: var(--gm-r-m);
  background: var(--gm-s-float); border: 1px solid var(--gm-l-gold); box-shadow: var(--gm-sh-float); z-index: var(--gm-z-ask); }
.gm-dlg > .gm-ph { margin-bottom: var(--gm-sp-section); }
.gm-dlg p { margin: 0; font-size: var(--gm-fs-body); line-height: 1.7; }
.gm-dlg em { font-style: normal; font-weight: 500; color: var(--gm-c-bright); }
.gm-dlg em.gm-coin { color: var(--gm-c-coin); }
.gm-ask-note { display: block; margin-top: var(--gm-sp-tight); font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-ask-input { display: block; width: 100%; margin-top: var(--gm-sp-inner); }
.gm-dlg .gm-say { margin-top: var(--gm-sp-near); }
.gm-ask-input + .gm-say + .gm-ask-note { margin-top: var(--gm-sp-hair); }
/* 设账号密码 (guest-bind.ts passwordBox): three labelled inputs, each with its line under it. */
.gm-pw-body { display: flex; flex-direction: column; }
.gm-pw-body .gm-ask-input { width: 100%; }
.gm-pw-body .gm-ask-note { margin-top: 0; }
.gm-pw-body .gm-say:empty { min-height: 0; }
.gm-dlg .gm-row { justify-content: flex-end; margin-top: var(--gm-sp-section); }
.gm-dlg .gm-btn.danger { background: var(--gm-s-bad); }
@media (hover: hover) { .gm-dlg .gm-btn.danger:hover { background: var(--gm-s-bad-hover); } }
.gm-dlg .gm-btn small { margin-left: var(--gm-sp-near); font-size: var(--gm-fs-foot); font-weight: 400; color: var(--gm-c-dim); }
/* 断开了 / 出错了: over everything, the game cannot go on under it. */
.gm-alarm { z-index: var(--gm-z-alarm); }
/* 绑定 Telegram (src/client/play/guest-bind.ts, docs/login.md「试玩账号」): the QR code for the phone. */
.gm-dlg.gm-bind { width: 380px; } /* its gold 「做」 (600) and 「取消」 with their keys on one row in English (面、线与按钮: a row of buttons never folds) */
.gm-bind-body { display: flex; flex-direction: column; align-items: center; gap: var(--gm-sp-row); text-align: center; }
.gm-bind-qr { width: 148px; height: 148px; padding: 6px; box-sizing: border-box; background: #fff; border-radius: var(--gm-r-s); } /* 材质：二维码要白底 */
.gm-bind-qr svg { display: block; width: 100%; height: 100%; }
.gm-guest { display: flex; flex-direction: column; gap: var(--gm-sp-near); padding: var(--gm-sp-inner); border-radius: var(--gm-r-m); border: 1px solid var(--gm-l-line); background: var(--gm-s-card);
  margin-bottom: var(--gm-sp-section); } /* 试玩账号的卡: the pass card's layout, a 线·框 edge (gold is the pass card's alone, 面板统一版式): a card's edge, 线·分隔 */
.gm-guest > b { font-size: var(--gm-fs-emph); font-weight: 600; }
.gm-guest p { margin: 0; line-height: 1.7; }
.gm-guest .gm-btn { align-self: flex-start; }
/* The season pass offer: above the skill bar, wider; it does not stop the game. */
.gm-pass { top: auto; bottom: 200px; transform: translateX(-50%); width: 480px; z-index: var(--gm-z-pop); }
@keyframes gm-shrink { from { width: 100%; } to { width: 0; } }
.gm-row { display: flex; gap: var(--gm-sp-row); align-items: center; flex-wrap: wrap; }
/* Buttons (用户选 B，2026-09-30, https://claude.ai/artifact/TrDWzij8dL5byC7pFQ2pK7; three sizes 2026-10-04): round, solid; md 28 by
   default, lg 32 for the main one of a page (gold) and the confirmation card, sm 22 in a table row. Danger is red words. */
.gm-btn { box-sizing: border-box; height: var(--gm-h-md); padding: 0 var(--gm-sp-section); border: 0; border-radius: var(--gm-r-full); background: var(--gm-s-raise);
  color: var(--gm-c-main); font-size: var(--gm-fs-body); line-height: var(--gm-h-md); white-space: nowrap; cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-btn:hover { background: var(--gm-s-hover); } }
/* Focus is the gold line (docs/ui-rules.md「面、线与按钮」, 规范检查 2026-10-12: a clicked 「绑定 Telegram」 wore the browser's blue ring);
   a mouse click shows none, the keyboard's focus shows the gold. */
.gm-btn:focus { outline: none; }
.gm-btn:focus-visible { outline: 1px solid var(--gm-l-gold); outline-offset: 1px; }
.gm-btn.lg, .gm-btn.gold { height: var(--gm-h-lg); line-height: var(--gm-h-lg); padding: 0 20px; }
.gm-btn.sm { height: var(--gm-h-sm); line-height: var(--gm-h-sm); padding: 0 var(--gm-sp-inner); font-size: var(--gm-fs-label); }
.gm-btn.gold { background: var(--gm-s-gold); color: var(--gm-c-gold); font-weight: 600; }
@media (hover: hover) { .gm-btn.gold:hover { background: var(--gm-s-gold-hover); } }
.gm-btn.gold.md { height: var(--gm-h-md); line-height: var(--gm-h-md); padding: 0 var(--gm-sp-section); } /* 商城卡片和道具行的「购买」：金色，比大号小一档；旁边的「送人」再小一档 */
.gm-btn.danger { color: var(--gm-c-bad); }
.gm-btn:disabled { opacity: var(--gm-off); cursor: default; background: var(--gm-s-raise); }
.gm-btn small { margin-left: var(--gm-sp-tight); font-size: var(--gm-fs-label); font-weight: 400; }
.gm-muted { color: var(--gm-c-dim); }
.gm-gold { color: var(--gm-c-gold); }
.gm-sub { color: var(--gm-c-sub); }
.gm-coin { color: var(--gm-c-coin); }
/* One line saying how it went, under the box or button it is about (docs/ui-rules.md「页面的四种状态」·错): 12, red when
   it failed, green when it worked; the box it is about gets a red edge. The canvas draws the same (FS.label). */
.gm-say { min-height: 1.5em; margin: 0; font-size: var(--gm-fs-label); line-height: 1.5; color: var(--gm-c-dim); }
.gm-say.good { color: var(--gm-c-good); }
.gm-say.bad { color: var(--gm-c-bad); }
.gm-guild-rule { margin-top: var(--gm-sp-tight); } /* 建行会: the name's length under its box, grey, wrapping (the placeholder only says 行会名) */
.gm-input.bad { border-color: var(--gm-c-bad); }
/* Waiting for the server (·等): nothing for the first --gm-t-mid, then 「读取中…」 where the content will be. */
.gm-wait { margin: 0; color: var(--gm-c-dim); opacity: 0; animation: gm-in var(--gm-t-quick) var(--gm-ease-out) var(--gm-t-mid) forwards; }
@keyframes gm-in { to { opacity: 1; } }
.gm-input { box-sizing: border-box; height: var(--gm-h-lg); padding: 0 var(--gm-sp-inner); border-radius: var(--gm-r-s); border: 1px solid var(--gm-l-edge); background: var(--gm-s-sunk); color: var(--gm-c-main); font: inherit; outline: none; user-select: text;
  -webkit-user-select: text; -webkit-touch-callout: default; } /* iPhone: a long press selects and copies (the page around says none) */
.gm-input:focus { border-color: var(--gm-l-gold); }
.gm-input.num { width: var(--gm-w-num); text-align: right; font-variant-numeric: tabular-nums; }
.gm-table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
/* a head and the cells under it: one padding, so the head's words stand over the column's (docs/ui-rules.md「间距」) */
.gm-table th { text-align: left; color: var(--gm-c-dim); font-weight: 400; font-size: var(--gm-fs-label); padding: var(--gm-sp-tight) var(--gm-sp-near); border-bottom: 1px solid var(--gm-l-line); }
.gm-table td { padding: var(--gm-sp-tight) var(--gm-sp-near); border-bottom: 1px solid var(--gm-l-faint); }
.gm-table td.r, .gm-table th.r { text-align: right; }
.gm-box { padding: var(--gm-sp-inner); border-radius: var(--gm-r-m); background: var(--gm-s-card); border: 1px solid var(--gm-l-line); display: flex; flex-direction: column; gap: var(--gm-sp-row); }
/* 金币 | 降龍币 switch (trade house: 寄售 per item, 买装备 filter) */
.gm-seg { box-sizing: border-box; flex: none; height: var(--gm-h-lg); display: inline-flex; position: relative; border-radius: var(--gm-r-full); overflow: hidden; vertical-align: middle; }
/* its edge drawn over the buttons, not round them: a finger on the edge is on a button (点得到: each half the whole 40 on a phone) */
.gm-seg::after { content: ""; position: absolute; inset: 0; border: 1px solid var(--gm-l-edge); border-radius: var(--gm-r-full); pointer-events: none; }
.gm-seg button { border: 0; background: none; color: var(--gm-c-dim); white-space: nowrap; padding: 0 var(--gm-sp-inner); cursor: pointer; font: inherit; transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-seg button:hover { background: var(--gm-s-raise); color: var(--gm-c-main); } }
.gm-seg button.on.gold { background: var(--gm-s-gold); color: var(--gm-c-gold); }
.gm-seg button.on.coins { background: var(--gm-s-coin); color: var(--gm-c-coin); }
.gm-seg button.on.all { background: var(--gm-s-gold); color: var(--gm-c-gold); } /* 「全部」 a control like the other halves: 面·金 (选择) */
/* 商城 → 充值 (sketch https://claude.ai/artifact/11ZgYCuVp7u3zEJU6w1T2s) */
/* 充值's tiers in even rows (ui.ts balanceGrid sets --gm-cols: as many a row as fit, 4 → 4 or 2 + 2, never 3 + 1), pushed
   down by the 送 N% badge's rise so the badge's top is 段 14 under the head's line like every page's first row (用户 2026-10-15
   批准草图 https://claude.ai/artifact/BwmTBVeXrQg9kW441i4DJQ ⑨) */
.gm-tiers { display: grid; grid-template-columns: repeat(var(--gm-cols, 2), minmax(0, 1fr)); gap: var(--gm-sp-near); margin-top: var(--gm-sp-row); }
.gm-tiers.gm-grid-measure > * { width: max-content; }
.gm-tiers > .gm-grid-rowstart { grid-column-start: 1; }
.gm-tier { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: var(--gm-sp-hair); padding: var(--gm-sp-inner); border-radius: var(--gm-r-m); border: 1px solid var(--gm-l-line); background: var(--gm-s-card); color: var(--gm-c-main); font: inherit; cursor: pointer; text-align: left; }
@media (hover: hover) { .gm-tier:hover { border-color: var(--gm-l-edge); } }
.gm-tier.on { border-color: var(--gm-l-gold); background: var(--gm-s-gold); }
.gm-tier .u { color: var(--gm-c-dim); font-size: var(--gm-fs-label); }
.gm-tier .c { color: var(--gm-c-coin); font-size: var(--gm-fs-emph); font-weight: 600; font-variant-numeric: tabular-nums; }
.gm-tier .c small { font-size: var(--gm-fs-label); font-weight: 400; }
.gm-tier .bonus { position: absolute; top: calc(-1px - var(--gm-sp-row)); right: var(--gm-sp-row); padding: 0 var(--gm-sp-tight); white-space: nowrap; border-radius: var(--gm-r-s); background: var(--gm-s-raise); color: var(--gm-c-bright); font-size: var(--gm-fs-label); } /* 8 over the card's outer edge (its border is 1) */
.gm-tier-other { flex: 1 1 0; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--gm-sp-row); padding: var(--gm-sp-inner); border-radius: var(--gm-r-m); border: 1px dashed var(--gm-l-line); }
/* 其他金额 and 充值 on one row, the button at its right, a thumb high (⑨); a narrow row folds inside the dashed box */
.gm-tier-pay { display: flex; align-items: center; gap: var(--gm-sp-row); }
.gm-tier-pay > .gm-btn.gold { flex: none; } /* the 大 32 on a computer, as high as the 其他金额 box beside it; a phone's 40 (.gm-page.phone .gm-btn) */
.gm-tier-amt { display: inline-flex; align-items: center; gap: var(--gm-sp-row); white-space: nowrap; }
.gm-tier-other.on { border: 1px solid var(--gm-l-gold); background: var(--gm-s-gold); }
.gm-topup-pay { display: grid; grid-template-columns: minmax(0, 1fr) 150px; gap: var(--gm-sp-section); align-items: start; }
.gm-topup-amt { font-size: 26px; font-weight: 600; font-variant-numeric: tabular-nums; } /* 字号例外：要付的金额，整页最要紧的一个数 */
.gm-topup-amt em { font-style: normal; color: var(--gm-c-bright); } /* 认人的四位小数：强调，不是「坏」 */
.gm-topup-line { display: flex; align-items: center; gap: var(--gm-sp-inner); }
.gm-topup-addr { font-family: var(--gm-mono); font-size: var(--gm-fs-label); }
/* Copy (docs/ui-rules.md「符号」·复制, src/portal/copy.ts copyChip): one capsule to tap, what it copies then the copy icon (字·次), no words;
   copied: the icon a green ✓ for --gm-t-copied; not copied: a box with the whole text, red edge, selected to long-press and copy. */
.gm-copy { box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--gm-sp-row); max-width: 100%; min-height: var(--gm-h-lg); padding: 0 var(--gm-sp-section);
  border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-full); background: var(--gm-s-raise); color: var(--gm-c-main); white-space: nowrap; cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
.gm-copy .ico { display: flex; flex: none; color: var(--gm-c-sub); }
.gm-copy.done .ico { color: var(--gm-c-good); }
@media (hover: hover) { .gm-copy:hover { background: var(--gm-s-hover); } }
.gm-copy:focus-visible { outline: 1px solid var(--gm-l-gold); outline-offset: 1px; }
.gm-copy.bad { display: block; min-width: 0; padding: var(--gm-sp-near) var(--gm-sp-inner); border-color: var(--gm-c-bad); border-radius: var(--gm-r-m); background: var(--gm-s-sunk); cursor: text;
  white-space: normal; word-break: break-all; user-select: text; -webkit-user-select: text; -webkit-touch-callout: default; } /* the whole address, a chain's hex has no words to keep */
.gm-page.phone .gm-copy { min-height: var(--gm-h-touch); } /* 点得到 */
.gm-topup-pay .gm-box { gap: var(--gm-sp-near); }
.gm-topup-pay p { font-size: var(--gm-fs-label); line-height: 1.5; }
.gm-topup-qr { display: flex; flex-direction: column; align-items: center; gap: var(--gm-sp-near); text-align: center; font-size: var(--gm-fs-label); }
.gm-topup-qr > div { width: 138px; height: 138px; padding: 4px; border-radius: var(--gm-r-s); background: #f3ead8; } /* 材质：二维码要浅底才扫得出 */
.gm-topup-qr svg { width: 100%; height: 100%; display: block; }
.gm-ok { color: var(--gm-c-good); font-size: var(--gm-fs-emph); }
a.gm-btn { display: inline-block; text-decoration: none; align-self: flex-start; }
/* Dying (src/client/play/death.ts): big words straight on the greying world, no box (方案 B, 2026-10-01). */
.gm-death { position: fixed; left: 0; right: 0; top: 30%; transform: translateY(-50%); text-align: center; pointer-events: none; }
.gm-death p { margin: 0; }
.gm-death-title { font-family: var(--gm-kai); font-weight: 700; font-size: 72px; line-height: 1.1;
  color: var(--gm-c-blood); text-shadow: 0 0 18px rgba(0, 0, 0, .95), 0 0 4px rgba(0, 0, 0, .9); /* 材质：死亡大字压在画面上的黑晕 */ }
.gm-death-rule { width: 340px; max-width: 70vw; height: 1px; margin: 10px auto 12px; background: linear-gradient(90deg, transparent, rgba(217, 180, 90, .65), transparent); } /* 材质：死亡大字下面渐隐的线 */
.gm-death-by { /* 字号例外：「被 X 杀死」黑体 20，场面，和「你死了」一起看（2026-10-10 用户同意） */ font-size: var(--gm-fs-title); color: var(--gm-c-main); text-shadow: 0 0 6px #000, 0 0 2px #000; /* 材质：同「你死了」的黑晕 */ }
.gm-death-back { margin-top: var(--gm-sp-section) !important; font-size: var(--gm-fs-emph); color: var(--gm-c-gold); font-variant-numeric: tabular-nums; text-shadow: 0 0 6px #000, 0 0 2px #000; /* 材质：同「你死了」的黑晕 */ }
.gm-death.gm-death-out { animation: gm-death-fade var(--gm-t-slow) var(--gm-ease-in) forwards; }
@keyframes gm-death-fade { to { opacity: 0; } }
@media (prefers-reduced-motion: no-preference) { .gm-death { animation: gm-death-in var(--gm-t-slow) var(--gm-ease-out) both; } } /* 例外：死亡是场面转换，进来也用慢 */
@keyframes gm-death-in { from { opacity: 0; transform: translateY(-46%); } }
/* The panel's DOM pages (src/client/play/panel-pages.ts): 行会, 商城, 充值, 交易行 laid into the frame
   the HUD draws (docs/hud.md "面板的标签"). No box of their own: the frame is the box. The page head is the
   one every page has (统一版式, 2026-10-02; the canvas pages draw the same, Hud.pageHead): 46 high from
   the frame's inner top, the title in 楷书 20, the page's state on the right, a line under it; the
   body starts 14 below the line and scrolls on its own. */
.gm-page { position: fixed; z-index: var(--gm-z-ui); transform-origin: 0 0; display: flex; flex-direction: column; }
.gm-page[hidden] { display: none; }
.gm-top { position: fixed; inset: 0; z-index: var(--gm-z-top); pointer-events: none; }
.gm-top canvas { pointer-events: none; }
.gm-page-head { flex: none; margin-bottom: var(--gm-sp-section); } /* the content 段 14 under the head's line, every page (面板统一版式) */
.gm-ph { height: var(--gm-h-head); display: flex; align-items: center; justify-content: space-between; gap: var(--gm-sp-inner); border-bottom: 1px solid var(--gm-l-line); }
.gm-ph > b { font: 700 var(--gm-fs-title)/1 var(--gm-kai); color: var(--gm-c-gold); white-space: nowrap; transform: translateY(-1px); }
/* The one letter spacing of the game (用户 2026-10-14「默认不加字距」, docs/ui-rules.md「字体规划」): a page's name in 楷书, in Chinese only (简体, 繁体; <html lang> is set by lang.ts) — a page's head, a dialog's, a phone sheet's head. */
:lang(zh) :is(.gm-ph, .gm-pm-npchead) > b { letter-spacing: var(--gm-track-title); }
.gm-ph > span { font-size: var(--gm-fs-label); color: var(--gm-c-dim); white-space: nowrap; }
/* a dialog is a fixed width: a long English state (「Training 18 minutes」) breaks onto a second line inside it, not out of it */
.gm-dlg > .gm-ph { height: auto; min-height: var(--gm-h-head); }
.gm-dlg > .gm-ph > span { min-width: 0; white-space: normal; text-align: right; line-height: 1.3; }
/* the pass card's head: its title a whole sentence, 「你有 X 降龍币」 never broken; no room beside the title, it goes to the next line, on the right (「面板统一版式」: 页头右边的状态放不下时折到下一行) */
.gm-pass > .gm-ph { box-sizing: border-box; flex-wrap: wrap; row-gap: var(--gm-sp-tight); padding: var(--gm-sp-inner) 0 var(--gm-sp-near); }
.gm-pass > .gm-ph > b { min-width: 0; white-space: normal; line-height: 1.2; }
.gm-pass > .gm-ph > span { margin-left: auto; white-space: nowrap; }
/* A state in pieces (交易行's two sums): too long for one line, the next piece goes under, on the right (版面 第 4 步). */
.gm-ph > span.gm-ph-parts { min-width: 0; display: flex; flex-wrap: wrap; justify-content: flex-end; }
.gm-ph-part { white-space: nowrap; }
.gm-ph-part + .gm-ph-part { margin-left: var(--gm-sp-tight); } /* the space before 「·」, which a flex item drops */
/* The head's numbers are 字·主; money keeps its own colour (金币 gold, 降龍币 coin) — a count is not money (规范检查 2026-10-07). */
.gm-ph > span b { font-size: var(--gm-fs-body); font-weight: 500; color: var(--gm-c-main); font-variant-numeric: tabular-nums; }
.gm-ph > span b.gm-gold { color: var(--gm-c-gold); }
.gm-ph > span b.gm-coin { color: var(--gm-c-coin); }
.gm-jump { display: flex; gap: var(--gm-sp-near); margin-top: var(--gm-sp-section); }
/* What an NPC said as its window opened (the 铁匠): under the head's line, 字·副 13, two lines at most. */
.gm-page .gm-talk { margin-top: var(--gm-sp-section); font-size: var(--gm-fs-body); line-height: 1.6; color: var(--gm-c-sub); }
.gm-talk + .gm-jump { margin-top: var(--gm-sp-inner); }
.gm-page-body { position: relative; flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; gap: var(--gm-sp-inner);
  padding: 0 var(--gm-sp-near) var(--gm-sp-inner) 0; margin-right: calc(-1 * var(--gm-sp-near)); overscroll-behavior: contain; }
/* On a computer the head's words, the head's line and the content end on one right edge, the scrollbar outside it
   (docs/ui-rules.md「面板统一版式」, 用户 2026-10-15 批准草图 https://claude.ai/artifact/BwmTBVeXrQg9kW441i4DJQ ⑩ B): its room
   always kept (scrollbar-gutter), the body as much wider than the page as the bar and the 6 before it are
   (--gm-sbw: the bar's width, measured by panel-pages.ts measureBar), so a page that scrolls and one that does
   not end where the head does. */
.gm-page:not(.phone) > .gm-page-body { scrollbar-gutter: stable; margin-right: calc(-1 * var(--gm-sp-near) - var(--gm-sbw, 0px)); }
.gm-page p { margin: 0; line-height: 1.6; }
/* 小组标题, the only heading in a page (2026-10-05 用户选方案一): 15 white, a line to the right, its note on the line's end. */
.gm-page-body > :first-child { margin-top: 0; }
:where(.gm-page-body, .gm-pcol) > * { flex-shrink: 0; } /* 正文放不下就滑，不挤扁里面的块（设置页的语言一行被压到 30，40 的按钮组冒出去；docs/ui-rules.md「窗口一律用网页」）。:where 不加分量：自己写了 flex 的块（手机两栏 .gm-pcols 靠缩来各自滑）照自己的 */
.gm-grp { margin: var(--gm-sp-tight) 0 calc(-1 * var(--gm-sp-tight)); display: flex; align-items: center; gap: var(--gm-sp-inner); font-size: var(--gm-fs-emph); font-weight: 500; color: var(--gm-c-main); }
.gm-grp::after { content: ""; flex: 1; height: 1px; background: var(--gm-l-line); order: 1; }
.gm-grp .gm-muted { order: 2; font-size: var(--gm-fs-label); font-weight: 400; }
.gm-nowrap { flex-wrap: nowrap; }
.gm-row.gm-nowrap > .gm-input { min-width: 0; } /* the box gives way to the buttons beside it, which never break (版面 第 4 步: 交易行的币种分段钮) */
.gm-grow { flex: 1; }
.gm-notice { flex: none; width: 100%; height: auto; padding: var(--gm-sp-near) var(--gm-sp-inner); line-height: 1.6; resize: none; box-sizing: border-box; }
.gm-notice-text { white-space: pre-wrap; line-height: 1.6; }
.gm-members tr.off td { color: var(--gm-c-dim); }
.gm-members td.r, .gm-apps td.r { white-space: nowrap; } /* the leader's two buttons side by side, never one over the other (版面 第 4 步) */
.gm-name-link { cursor: pointer; border-radius: var(--gm-r-s); transition: background var(--gm-t-quick) var(--gm-ease-out); }
/* 能点的东西一律是 button (docs/ui-rules.md「按钮」, 用户 2026-10-16 草图 D5): a member's name, a skill card, a phone 交易行 row
   look as they did (no button's own border, ground, padding, colour or centring), the keyboard reaches them, its focus the
   1-pixel gold line */
button.gm-name-link { padding: 0; border: 0; background: none; color: inherit; text-align: inherit; }
:is(button.gm-skill, button.gm-mk-row) { width: 100%; color: inherit; text-align: left; }
.gm-name-link:focus, .gm-skill:focus, .gm-mk-row:focus { outline: none; }
.gm-name-link:focus-visible, .gm-skill:focus-visible, .gm-mk-row:focus-visible { outline: 1px solid var(--gm-l-gold); outline-offset: 1px; }
@media (hover: hover) { .gm-name-link:hover { background: var(--gm-s-raise); } }
/* 小牌子 (docs/ui-rules.md「条与角标」): 小 3, as high as its words, 12, 左右 紧 4; 金 = yours / in use (会长, 「使用中」), 凸 = plain
   information (副会长, 宣战, 「被动」, 「送 N%」). Round ends only for the chips one taps. */
.gm-role { margin-left: var(--gm-sp-tight); padding: 0 var(--gm-sp-tight); border-radius: var(--gm-r-s); white-space: nowrap; font-size: var(--gm-fs-label); background: var(--gm-s-gold); color: var(--gm-c-gold); }
.gm-role.vice { background: var(--gm-s-raise); color: var(--gm-c-sub); }
/* 行会战: who declared (docs/game.md「行会战」) */
.gm-role.war { background: var(--gm-s-raise); color: var(--gm-c-main); } /* 我方宣战：不是坏事，不用红（2026-10-05 规范检查） */
.gm-role.war.them { background: var(--gm-s-raise); color: var(--gm-c-dim); }
.gm-members tr.off .gm-role { opacity: var(--gm-off); }
.gm-foot { justify-content: flex-end; margin-top: auto; padding-top: var(--gm-sp-tight); }
.gm-pass-card { display: flex; flex-direction: column; gap: var(--gm-sp-near); padding: var(--gm-sp-inner); border-radius: var(--gm-r-m); border: 1px solid var(--gm-l-gold);
  background: linear-gradient(135deg, rgba(230, 200, 138, .13), rgba(0, 0, 0, .35)); } /* 材质：通行证卡片的金光 */
.gm-pass-card > b { font-size: var(--gm-fs-emph); font-weight: 600; }
.gm-looks { display: grid; grid-template-columns: repeat(auto-fill, minmax(max(var(--gm-card-min), (100% - 2 * var(--gm-sp-near)) / 3 - 1px), 1fr)); grid-auto-rows: 1fr; gap: var(--gm-sp-near); } /* every card as tall as the tallest: a note on two or three lines (日文) leaves no row uneven (版面 第 4 步) */
.gm-looks.pet { grid-template-columns: repeat(auto-fill, minmax(max(var(--gm-card-min), (100% - var(--gm-sp-near)) / 2 - 1px), 1fr)); } /* as many as fit at --gm-card-min, three at most (宝宝 two): the - 1px keeps the count from rounding down */
/* 技能: one stage, the picked look cast on black (src/client/play/skill-stages.ts); the price and 购买 under it; the names below */
.gm-stage { flex: none; aspect-ratio: 3 / 2; background: #000; border: 1px solid var(--gm-l-faint); border-radius: var(--gm-r-s); /* 材质：技能外观的黑舞台 */ overflow: hidden; }
.gm-stage canvas { display: block; width: 100%; height: 100%; }
.gm-stage-foot { flex: none; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--gm-sp-tight) var(--gm-sp-inner); min-height: var(--gm-h-lg); } /* as high as the 大 购买 in it */
.gm-stage-foot > b { min-width: 0; font-size: var(--gm-fs-emph); font-weight: 600; }
/* the price and buttons: one piece on the right; a long name (English) puts it on a line of its own (版面 第 4 步) */
.gm-stage-deal { margin-left: auto; display: flex; align-items: center; gap: var(--gm-sp-inner); white-space: nowrap; }
.gm-names { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--gm-sp-near); }
.gm-names-job { grid-column: 1 / -1; margin: var(--gm-sp-near) 0 var(--gm-sp-hair); font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-names-job:first-child { margin-top: 0; }
.gm-name { display: flex; align-items: baseline; gap: var(--gm-sp-row); min-width: 0; padding: var(--gm-sp-tight) var(--gm-sp-row); border: 0; border-radius: var(--gm-r-s); background: none;
  color: var(--gm-c-main); font: var(--gm-fs-body) var(--gm-hei); text-align: left; cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-name:hover { background: var(--gm-s-raise); } }
.gm-name.on { color: var(--gm-c-gold); } /* 面·金 and the bar: 选中的行 below */
.gm-name:focus-visible { outline: 1px solid var(--gm-l-gold); }
.gm-name > span { flex: 1; min-width: 0; overflow-wrap: anywhere; } /* a name too long for its column takes a second line, never cut (版面 第 4 步) */
.gm-name > i { flex: none; font-style: normal; font-size: var(--gm-fs-label); white-space: nowrap; } /* the small tag stays one piece; the name wraps beside it */
.gm-look-card { display: flex; flex-direction: column; gap: var(--gm-sp-tight); min-width: 0; padding: var(--gm-sp-near); border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m);
  background: linear-gradient(rgba(40, 32, 22, .7), rgba(10, 8, 6, .6)); } /* 材质：外观卡片 */
@media (hover: hover) { .gm-look-card:hover { border-color: var(--gm-l-edge); } }
.gm-look-card > b { font-weight: 600; font-size: var(--gm-fs-body); line-height: 1.35; }
.gm-look-pic { display: flex; justify-content: center; align-items: flex-end; aspect-ratio: 1; border-radius: var(--gm-r-s);
  background: radial-gradient(65% 55% at 50% 55%, rgba(230, 200, 138, .1), rgba(0, 0, 0, .45)); } /* 材质：外观底下的光 */
.gm-look-pic canvas { width: 100%; height: 100%; object-fit: contain; }
.gm-looks.pet .gm-look-pic { aspect-ratio: 5 / 4; padding: var(--gm-sp-tight) var(--gm-sp-hair); overflow: hidden; }
.gm-pet-grid { display: grid; width: 100%; align-self: stretch; justify-content: center; column-gap: 3%; align-items: end; justify-items: center; }
.gm-pet-grid canvas { width: 150%; height: auto; }
.gm-pet-lab { color: var(--gm-c-dim); font-size: var(--gm-fs-label); line-height: 1.3; min-height: 1.3em; padding-top: var(--gm-sp-hair); }
.gm-look-note { font-size: var(--gm-fs-label); line-height: 1.4; }
/* The price on its own line, then 送人 (small, its own width) and 购买 (gold, the rest of the line) side by side
   (用户，2026-10-05: in one line with the price they ran out of a card three to a row); owned: 已拥有 takes the
   price's line and 送人 stands alone at the left (docs/hud.md「商城页」). */
.gm-look-foot { display: flex; flex-direction: column; gap: var(--gm-sp-tight); margin-top: auto; padding-top: var(--gm-sp-hair); font-size: var(--gm-fs-label); }
.gm-look-btns { display: flex; align-items: center; gap: var(--gm-sp-row); }
.gm-look-foot .gm-look-btns > .gm-btn.gold { flex: 1; }
.gm-look-foot .gm-btn { min-width: 0; }
.gm-page.phone .gm-look-foot .gm-look-btns > .gm-btn.gold { flex: none; } /* a phone's buttons are as wide as their words (面、线与按钮) */
.gm-keep { white-space: nowrap; } /* a name in 「」 does not break in two */
/* 道具 (商城价格, 改名卡): one row each, the picture, the name and what it is, the price and the buttons on the right. */
.gm-shop-items { display: flex; flex-direction: column; gap: var(--gm-sp-near); } /* 道具: one block of cards, 6 apart */
.gm-shop-item { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gm-sp-inner); padding: var(--gm-sp-near); border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m); background: var(--gm-s-card); }
@media (hover: hover) { .gm-shop-item:hover { border-color: var(--gm-l-edge); } }
.gm-shop-item .gm-pic { width: 52px; height: 52px; }
.gm-shop-item-mid { flex: 1 1 9em; min-width: 0; display: flex; flex-direction: column; gap: var(--gm-sp-tight); }
.gm-shop-item-mid > b { font-size: var(--gm-fs-emph); font-weight: 600; }
.gm-shop-item-desc { font-size: var(--gm-fs-label); color: var(--gm-c-sub); }
.gm-shop-item-end { flex: none; margin-left: auto; display: flex; flex-direction: column; align-items: flex-end; gap: var(--gm-sp-near); font-size: var(--gm-fs-body); } /* a narrow phone: the name and words keep 9em, the price and buttons fold under them on the right (换行: a name never down to one 字 a line) */
/* 送人 (docs/money.md「送人」): the recipient page's rows, one person each: name, 〈guild〉, 「已有」 on the right when
   they have it. Like the guild page, one known to be offline is dim (字·弱), as is one who has it, who does nothing. */
.gm-who-list { display: flex; flex-direction: column; }
.gm-who { box-sizing: border-box; display: flex; align-items: center; gap: var(--gm-sp-row); width: 100%; height: var(--gm-row-h); padding: 0 var(--gm-sp-inner); border: 0; border-radius: var(--gm-r-s); background: none;
  color: var(--gm-c-main); font: var(--gm-fs-body) var(--gm-hei); text-align: left; cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-who:hover { background: var(--gm-s-raise); } }
.gm-who:focus-visible { outline: 1px solid var(--gm-l-gold); }
.gm-who > b { font-weight: 500; }
.gm-who.off, .gm-who.has { color: var(--gm-c-dim); }
.gm-who.has { cursor: default; }
@media (hover: hover) { .gm-who.has:hover { background: none; } }
.gm-who-g { color: var(--gm-c-dim); font-size: var(--gm-fs-label); }
.gm-who-st { margin-left: auto; font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-page .gm-topup-pay { grid-template-columns: minmax(0, 1fr) 120px; gap: var(--gm-sp-inner); }
.gm-page .gm-topup-qr > div { width: 112px; height: 112px; }
.gm-page-body > .gm-btn.gold { align-self: flex-start; }
.gm-saved { margin-left: var(--gm-sp-row); font-size: var(--gm-fs-label); font-weight: 400; color: var(--gm-c-good); animation: gm-out var(--gm-t-slow) var(--gm-ease-in) var(--gm-t-hold) forwards; }
@keyframes gm-out { to { opacity: 0; } }
/* 交易行 (CLAUDE.md "交易行", sketch https://claude.ai/artifact/31Dc4onDL12JdTJmv1BPXS): the shelf,
   the slot that unfolds under its row, and 买东西. */
.gm-pic { width: 34px; height: 34px; flex: none; }
.gm-shelf { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--gm-sp-near); }
.gm-sl { position: relative; height: 76px; border-radius: var(--gm-r-s); border: 1px solid var(--gm-l-line); background: var(--gm-s-sunk); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--gm-sp-hair); min-width: 0; padding: 0 var(--gm-sp-tight); } /* 紧 4 each side: the grey words keep off the edge */
.gm-sl.click { cursor: pointer; }
.gm-holding .gm-sl.empty.click { cursor: default; } /* putting the thing in hand down is a game action: the arrow */
@media (hover: hover) { .gm-sl.click:hover { border-color: var(--gm-l-edge); } }
.gm-sl .pr { font-size: var(--gm-fs-label); font-variant-numeric: tabular-nums; white-space: nowrap; }
.gm-sl .t { font-size: var(--gm-fs-label); color: var(--gm-c-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.gm-sl.empty { border-style: dashed; }
.gm-sl.empty .t { color: var(--gm-c-off); }
.gm-sl.sold { border-color: var(--gm-l-edge); } /* 线·框 like 取回: gold is for the picked cell (.on); its 「已卖出」 is the gold */
.gm-sl .t.gm-gold { color: var(--gm-c-gold); }
.gm-sl.back { border-color: var(--gm-l-edge); }
.gm-sl.on { border-color: var(--gm-l-gold); outline: var(--gm-sel-w) solid var(--gm-l-gold); outline-offset: calc(-1 * var(--gm-sel-w)); } /* a picked cell: 2 of 线·金 round it, as an item cell's .sel (选择) */
.gm-sl.on::after { content: ""; position: absolute; left: 50%; bottom: -11px; margin-left: calc(-1 * var(--gm-sp-near)); border: 6px solid transparent; border-bottom-color: var(--gm-l-edge); }
.gm-unfold { grid-column: 1 / -1; margin: var(--gm-sp-tight) 0 var(--gm-sp-hair); padding: var(--gm-sp-inner); border-radius: var(--gm-r-m); background: var(--gm-s-card); border: 1px solid var(--gm-l-line); display: flex; flex-direction: column; gap: var(--gm-sp-row); font-size: var(--gm-fs-body); }
.gm-unfold .gm-row { flex-wrap: nowrap; }
.gm-page.phone .gm-unfold .gm-row { flex-wrap: wrap; } /* a phone's column is narrow: what does not fit goes on the next line (点得到: 取消 and the box are there to tap) */
.gm-page.phone .gm-unfold .gm-seg { height: auto; max-width: 100%; flex-wrap: wrap; overflow: visible; } /* wider than the column (a long word in a narrow column; none of today's at 667 × 360): its second half on the next line, each 40, no word broken */
.gm-page.phone .gm-unfold .gm-seg button { flex: 1 0 auto; height: var(--gm-h-touch); }
.gm-page.phone .gm-unfold .gm-seg { gap: var(--gm-sp-tight); } .gm-page.phone .gm-unfold .gm-seg::after { content: none; } /* it may fold onto two lines: two pills, each its own edge, never one tall round frame (按钮一律胶囊) */
.gm-page.phone .gm-unfold .gm-seg button { border: 1px solid var(--gm-l-edge); border-radius: var(--gm-r-full); }
.gm-end { justify-content: flex-end; }
.gm-small { font-size: var(--gm-fs-label); }
.gm-chips { display: flex; flex-wrap: wrap; gap: var(--gm-sp-near); } /* chips side by side: 6, like the shop's jump row (用户同意 2026-10-10) */
/* chips in even lines (ui.ts balanceChips, 交易行's kinds): the box a column of lines, as far apart as the box's own rows */
.gm-chips.gm-chip-rows { flex-direction: column; flex-wrap: nowrap; align-items: flex-start; }
.gm-chip-row { display: flex; flex-wrap: nowrap; column-gap: var(--gm-sp-near); max-width: 100%; }
/* its words in its exact middle (对齐和量位置, 用户 2026-10-17 看草图第七张：都照推荐): centred as a flex item one line of the
   font's own height, not by a 22 line-height, whose half-leading the browser rounds down — the words then sat half a pixel high */
.gm-chip { box-sizing: border-box; height: var(--gm-h-sm); display: inline-flex; align-items: center; justify-content: center; line-height: normal; padding: 0 var(--gm-sp-inner); border: 0; white-space: nowrap; flex: none; border-radius: var(--gm-r-full); background: var(--gm-s-raise); color: var(--gm-c-dim); font-size: var(--gm-fs-label); cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-chip:hover { background: var(--gm-s-hover); color: var(--gm-c-main); } }
.gm-chip.on { background: var(--gm-s-gold); color: var(--gm-c-gold); } /* 选中的字一律金·题: chip, segment, key cap, tab (用户 2026-10-16) */
.gm-chips.gm-sort { margin-left: auto; flex-wrap: nowrap; } /* the order (最便宜 / 最新): two chips at the row's right end, both written, the current one lit (docs/ui-rules.md「选择」) */
.gm-lots { display: flex; flex-direction: column; } /* 买东西: one block; the rows apart by their own padding and line */
.gm-lot { display: flex; align-items: center; gap: var(--gm-sp-row); padding: var(--gm-sp-near) 0; border-bottom: 1px solid var(--gm-l-faint); }
.gm-lot-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.gm-lot-text > span:first-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-lot-text .gm-muted { font-size: var(--gm-fs-label); }
/* 技能 (src/client/play/skill-page.ts): two columns of cards; picked = gold, the bar's slots light up. */
.gm-skills { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gm-sp-near); }
.gm-skill { position: relative; display: flex; align-items: center; gap: var(--gm-sp-inner); min-height: 54px; box-sizing: border-box; padding: var(--gm-sp-near); border: 1px solid var(--gm-l-line);
  border-radius: var(--gm-r-m); background: var(--gm-s-card); cursor: pointer; user-select: none; }
@media (hover: hover) { .gm-skill:hover { border-color: var(--gm-l-edge); } }
.gm-skill.on { border-color: var(--gm-l-gold); background: var(--gm-s-gold); }
.gm-skill-ic { flex: none; display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; box-sizing: border-box;
  border: 1px solid var(--gm-l-edge); background: var(--gm-s-sunk); }
.gm-skill-ic canvas { width: 36px; height: 34px; }
.gm-skill-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--gm-sp-near); }
.gm-skill-mid > b { font-weight: 600; } /* a name too long for one line takes a second, between words, never cut (docs/ui-rules.md「换行」); the card grows with it */
.gm-skill-lv { display: flex; align-items: center; gap: var(--gm-sp-near); min-height: 8px; padding-left: var(--gm-sp-hair); } /* the diamonds, then 被动 (a passive's tag) or the training bar */
.gm-skill-lv > i { flex: none; width: 6px; height: 6px; box-sizing: border-box; border: 1px solid var(--gm-l-edge); transform: rotate(45deg); }
.gm-skill-lv > i.on { border-color: var(--gm-l-gold); background: var(--gm-l-gold); }
.gm-train { flex: 1; height: 2px; margin-left: var(--gm-sp-hair); background: var(--gm-s-raise); }
.gm-train > i { display: block; height: 100%; background: var(--gm-l-edge); } /* our own bar: 线·框 (规范检查 2026-10-10: was marked 材质) */
.gm-train.slow > i { background: var(--gm-c-off); } /* dull while the character's level is short */
.gm-skill-tag { flex: none; margin-left: var(--gm-sp-hair); padding: 0 var(--gm-sp-tight); border-radius: var(--gm-r-s); background: var(--gm-s-raise); font-size: var(--gm-fs-label); color: var(--gm-c-sub); white-space: nowrap; } /* 小牌子·凸 (条与角标), like 副会长 */
.gm-skill-hint { display: block; font-size: var(--gm-fs-label); line-height: 1.5; color: var(--gm-c-off); /* the card's last line, grey (提示的去处 ③): the card grows by it, and back once it has faded (skill-page.ts redraws) */
  animation: gm-out var(--gm-t-slow) var(--gm-ease-in) var(--gm-t-hold) forwards; }
/* 收购 / 买卖 (src/client/play/sell-page.ts): today's lot on a grid like the storage's, then what was sold. */
.gm-bad { color: var(--gm-c-bad); }
.gm-sell-grid { position: relative; flex: none; align-self: center; background-repeat: repeat; }
/* An item on its cells, drawn like the bag's (src/client/play/item-cells.ts): the cell picture behind, the bag picture centred. */
.gm-cell-it { position: relative; flex: none; box-sizing: border-box; display: flex; align-items: center; justify-content: center; background-size: 100% 100%; }
.gm-cell-it canvas { pointer-events: none; }
.gm-cell-it .gm-cell-short { position: absolute; inset: 0; background-size: 100% 100%; pointer-events: none; }
.gm-sell-it { position: absolute; } /* 物品是箭头（docs/ui-rules.md「叠放顺序和鼠标」）*/
/* 角标 (docs/ui-rules.md「格子的意图语言」): a count at the lower right, 注脚 11 bold 字·主 with the dark edge words on a picture have;
   a mark (a dot, a chip) at the upper right with --gm-sh-badge. One kind on every cell, the pages' and the phone's */
.gm-cell-it small { position: absolute; right: var(--gm-sp-tight); bottom: var(--gm-sp-hair); font-size: var(--gm-fs-foot); font-weight: 700; color: var(--gm-c-main); text-shadow: var(--gm-sh-text); }
/* 查看装备 (src/client/play/inspect-page.ts): his eight slots placed like the character page's gear block. */
.gm-gear { position: relative; flex: none; align-self: center; }
.gm-gear-slot { position: absolute; } /* 物品是箭头（docs/ui-rules.md「叠放顺序和鼠标」）*/
.gm-sell-back { display: flex; flex-direction: column; gap: var(--gm-sp-near); }
.gm-sell-row { display: flex; align-items: center; gap: var(--gm-sp-inner); padding: var(--gm-sp-near); border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m); background: var(--gm-s-card); }
@media (hover: hover) { .gm-sell-row:hover { border-color: var(--gm-l-edge); } }
.gm-sell-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--gm-sp-hair); }
.gm-sell-mid > b { font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.gm-sell-mid > span { font-size: var(--gm-fs-label); }
/* 告示栏 (src/client/play/board-page.ts, docs/hud.md「告示栏」): 写意见 pinned under the head, a box like the guild's 公告, the
   warning, the count and 交上去 under it; the tabs with 最近 N 天 on their right; each post a like box, the text, who and
   when under it; rows apart by their padding and a faint line, like 买东西. */
.gm-board-write { display: flex; flex-direction: column; gap: var(--gm-sp-near); margin-top: var(--gm-sp-section); }
.gm-board-write .gm-grp { margin: 0; }
.gm-board-write .gm-input:disabled { cursor: default; }
.gm-board-write .gm-input:disabled::placeholder { color: var(--gm-c-dim); } /* 写满了 / 禁言: said in the box, readable */
.gm-board-foot { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--gm-sp-inner); margin-top: var(--gm-sp-hair); } /* 6 + 2: 交上去 8 off the box (点得到) */
.gm-board-foot > .gm-muted { flex: 1 1 12em; min-width: 0; font-size: var(--gm-fs-label); line-height: 1.5; } /* a narrow column: the count and 提交 fold under it on the right (换行: never a 字 a line) */
.gm-board-count { flex: none; font-size: var(--gm-fs-label); color: var(--gm-c-dim); font-variant-numeric: tabular-nums; }
.gm-board-days { margin-left: auto; align-self: center; font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-posts { display: flex; flex-direction: column; }
.gm-post { display: flex; align-items: flex-start; gap: var(--gm-sp-row); padding: var(--gm-sp-near) 0; border-bottom: 1px solid var(--gm-l-faint); }
.gm-like { flex: none; width: 40px; box-sizing: border-box; padding: var(--gm-sp-tight) 0; display: flex; flex-direction: column; align-items: center; gap: var(--gm-sp-hair);
  border: 1px solid var(--gm-l-edge); border-radius: var(--gm-r-full); background: var(--gm-s-raise); color: var(--gm-c-dim); font: inherit; font-size: var(--gm-fs-label); cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); } /* a button: 凸, pointed at 悬停 (指着就升一档) */
.gm-like b { font-size: var(--gm-fs-body); font-weight: 500; color: var(--gm-c-main); font-variant-numeric: tabular-nums; }
@media (hover: hover) { .gm-like:hover { background: var(--gm-s-hover); } }
.gm-like.on { border-color: var(--gm-l-gold); color: var(--gm-c-gold); }
.gm-like.on b { color: var(--gm-c-gold); }
.gm-like:disabled { opacity: var(--gm-off); cursor: default; background: var(--gm-s-raise); }
.gm-post-mid { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--gm-sp-hair); }
.gm-post-mid > p { overflow-wrap: anywhere; }
.gm-post-meta { display: flex; flex-wrap: wrap; gap: 0 var(--gm-sp-row); font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-good { color: var(--gm-c-good); }
/* the owner's reply under a post (docs/hud.md「告示栏」): the page's ground in a line edge, 开发者 in gold, then the words */
.gm-post-reply { margin-top: var(--gm-sp-tight); padding: var(--gm-sp-inner); background: none; border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m); display: flex; flex-direction: column; gap: var(--gm-sp-hair); } /* on the page's own ground, an edge like a card's (用户 2026-10-15：「纯黑很丑 不对的 应该和那个页面的面板底色一致」) */
.gm-post-reply > p { overflow-wrap: anywhere; }
.gm-post-reply-who { display: flex; gap: var(--gm-sp-row); font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
/* 设置 (src/client/play/settings-page.ts, docs/hud.md「设置页」): two columns of rows, the name left and the switch or
   keycaps right; the row under the mouse lights up. */
.gm-set-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--gm-sp-section); }
.gm-set-col { display: flex; flex-direction: column; min-width: 0; }
.gm-set-row { box-sizing: border-box; min-height: var(--gm-row-h); display: flex; align-items: center; justify-content: space-between; gap: var(--gm-sp-row); padding: 0 var(--gm-sp-near); margin: 0 calc(-1 * var(--gm-sp-near));
  border: 0; border-bottom: 1px solid var(--gm-l-faint); border-radius: 0; background: none; color: var(--gm-c-main); font: inherit; text-align: left; cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
div.gm-set-row { cursor: default; }
.gm-set-solo { min-height: var(--gm-row-h); display: flex; align-items: center; } /* 一组只有一个控件（语言）：不是行，不整行亮，只亮指着的那个按钮（docs/hud.md「设置页」） */
@media (hover: hover) { .gm-set-row:hover { background: var(--gm-s-raise); } } /* 指着升一档：行本身没有底，升到凸 */
.gm-set-row > span:first-child { min-width: 0; padding: var(--gm-sp-hair) 0; line-height: 1.3; } /* a long name (English, 日文) takes two lines, never cut (版面 第 4 步) */
.gm-switch { flex: none; position: relative; box-sizing: border-box; width: 34px; height: 18px; border-radius: var(--gm-r-full); border: 1px solid var(--gm-l-edge); background: var(--gm-s-card); }
.gm-switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 10px; height: 10px; border-radius: var(--gm-r-full); background: var(--gm-l-edge); }
.gm-switch.on { border-color: var(--gm-l-gold); background: var(--gm-s-gold); }
.gm-switch.on::after { left: auto; right: 3px; background: var(--gm-l-gold); }
/* 开关 (docs/ui-rules.md「选择」): 34 × 18, never tapped by itself — the whole row is (.gm-set-row: a <button>, or a <label> round a
   hidden checkbox and the switch: its hand, its hover and its press are the row's), 32 high on a computer, 40 on a phone; the
   checkbox's state shows on the switch beside it */
label.gm-set-row { position: relative; }
.gm-set-row > input[type="checkbox"] { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: inherit; } /* the real tap target is the whole row (点得到); its hand, hover and press are the row's */
.gm-set-row > input:checked + .gm-switch { border-color: var(--gm-l-gold); background: var(--gm-s-gold); }
.gm-set-row > input:checked + .gm-switch::after { left: auto; right: 3px; background: var(--gm-l-gold); }
.gm-set-row:focus-visible, .gm-set-row:has(> input:focus-visible) { outline: 1px solid var(--gm-l-gold); outline-offset: -1px; }
/* 滑动条 (docs/ui-rules.md「选择」): a native <input type="range" class="gm-range"> in our colours. The track a 1-pixel 线·框 line,
   the part up to the value 金 (2 thick; WebKit reads --gm-range-at, the value's share, set by whoever moves it; Firefox draws its
   own progress); the thumb round, 面·底 with a 线·金 edge, 16 on a computer, 24 on a phone; the box you can grab 24 high on a
   computer, 40 on a phone (点得到). Pointed at or pressed, the thumb goes up one step (底 → 凸, 指着就升一档); focused from the
   keyboard, the gold line round the box. Nobody uses it yet. */
.gm-range { -webkit-appearance: none; appearance: none; box-sizing: border-box; display: block; width: 100%; height: 24px; margin: 0; padding: 0; background: none; --gm-range-at: 0%; --gm-range-thumb: 16px; } /* the browser's own arrow: a hand would ask for a hover on the whole box */
/* WebKit: the track as tall as the thumb (so it sits in the middle), its two lines drawn across its middle */
.gm-range::-webkit-slider-runnable-track { height: var(--gm-range-thumb); background: linear-gradient(var(--gm-c-gold), var(--gm-c-gold)) 0 50% / var(--gm-range-at) 2px no-repeat, linear-gradient(var(--gm-l-edge), var(--gm-l-edge)) 0 50% / 100% 1px no-repeat; }
.gm-range::-moz-range-track { height: 1px; background: var(--gm-l-edge); }
.gm-range::-moz-range-progress { height: 2px; background: var(--gm-c-gold); }
.gm-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; box-sizing: border-box; width: var(--gm-range-thumb); height: var(--gm-range-thumb); border: 1px solid var(--gm-l-gold); border-radius: var(--gm-r-full); background: var(--gm-s-card); transition: background var(--gm-t-quick) var(--gm-ease-out); }
.gm-range::-moz-range-thumb { box-sizing: border-box; width: var(--gm-range-thumb); height: var(--gm-range-thumb); border: 1px solid var(--gm-l-gold); border-radius: var(--gm-r-full); background: var(--gm-s-card); transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-range:hover::-webkit-slider-thumb { background: var(--gm-s-raise); } .gm-range:hover::-moz-range-thumb { background: var(--gm-s-raise); } }
.gm-range:active::-webkit-slider-thumb { background: var(--gm-s-raise); }
.gm-range:active::-moz-range-thumb { background: var(--gm-s-raise); }
.gm-range:focus { outline: none; }
.gm-range:focus-visible { outline: 1px solid var(--gm-l-gold); outline-offset: 1px; }
.gm-range:disabled { opacity: var(--gm-off); cursor: default; }
/* 多行文本框的字数 (docs/ui-rules.md「输入框和键盘」): 「12 / 200」 in the box's lower right corner, 12 字·弱; over the limit 坏.
   Its box wraps both: <div class="gm-count-box"><textarea class="gm-input …"></textarea><span class="gm-count">12 / 200</span></div>;
   the text keeps clear of it by the box's own bottom padding */
.gm-count-box { position: relative; }
.gm-count-box > textarea { display: block; padding-bottom: var(--gm-sp-page); } /* block: an inline box leaves a line's descent under it, and the count (anchored to the wrapper's foot) sat on the box's border (用户 2026-10-17); 18 = the count's 12 + its 6 up */
.gm-count { position: absolute; right: var(--gm-sp-inner); bottom: var(--gm-sp-near); font-size: var(--gm-fs-label); line-height: 1; color: var(--gm-c-dim); font-variant-numeric: tabular-nums; pointer-events: none; }
.gm-count.bad { color: var(--gm-c-bad); }
.gm-caps { flex: none; display: flex; gap: var(--gm-sp-tight); }
/* 键帽 (docs/ui-rules.md「选择」): one kind on the pages — 设置's keys to change (.gm-cap, a button), the 技能 page's key of a
   card's slot (.gm-skill-key), 固定按键's keys (kbd): 22 high, at least as wide, 小 3, 线·框 on 面·底, 12; picked gold, none
   set 面·凹 and 字·暗 (用户 2026-10-17 看草图 https://claude.ai/artifact/NrxvbyactM3YdGmpSTGpfx 第六张, 照推荐) */
.gm-cap, .gm-skill-key, .gm-set-fixed kbd { box-sizing: border-box; flex: none; display: inline-flex; align-items: center; justify-content: center; min-width: var(--gm-h-sm); height: var(--gm-h-sm); padding: 0 var(--gm-sp-near);
  border: 1px solid var(--gm-l-edge); border-radius: var(--gm-r-s); background: var(--gm-s-card); color: var(--gm-c-main); font: var(--gm-fs-label) var(--gm-hei); white-space: nowrap; }
.gm-cap { cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
@media (hover: hover) { .gm-cap:hover { background: var(--gm-s-raise); } } /* 底 → 凸，一档 */
.gm-cap.on { border-color: var(--gm-l-gold); background: var(--gm-s-gold); color: var(--gm-c-gold); }
.gm-cap.none, .gm-skill-key.none { border-color: var(--gm-l-line); background: var(--gm-s-sunk); color: var(--gm-c-off); }
.gm-set-fixed { margin: 0; display: flex; flex-wrap: wrap; gap: var(--gm-sp-near) var(--gm-sp-section); font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-set-fixed > span { display: inline-flex; align-items: center; gap: var(--gm-sp-near); }
.gm-set-foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: var(--gm-sp-inner); padding-top: var(--gm-sp-section); }
.gm-set-leave { display: flex; align-items: center; justify-content: space-between; gap: var(--gm-sp-row); } /* a phone's 回选人, right under its group's title */
.gm-set-note { margin: 0; font-size: var(--gm-fs-label); line-height: 1.5; color: var(--gm-c-dim); } /* a 灰注 (what 回选人 does), not a result: .gm-say is only for what came of something (页面的四种状态) */
/* 外观 (src/client/play/looks-page.ts, docs/hud.md「外观页」): 装备 and 宝宝 places as small boxes (what each wears now), the
   chosen one gold; the owned looks for it as cards, three to a row, the picture square on top and the name under it.
   技能 (草图 B): rows without a box, the stage and name-only buttons on the right (below). */
.gm-lk-places { display: grid; gap: var(--gm-sp-near); }
.gm-lk-place { box-sizing: border-box; min-width: 0; height: 44px; display: flex; flex-direction: column; justify-content: center; align-items: flex-start; gap: var(--gm-sp-hair); padding: 0 var(--gm-sp-inner);
  border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m); background: var(--gm-s-card); color: var(--gm-c-sub); font: var(--gm-fs-body) var(--gm-hei); text-align: left; cursor: pointer; }
.gm-lk-place > span { font-size: var(--gm-fs-label); }
.gm-lk-place > b { max-width: 100%; font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.gm-lk-place > b.worn { color: var(--gm-c-main); }
@media (hover: hover) { .gm-lk-place:hover { border-color: var(--gm-l-edge); } }
.gm-lk-place.on { border-color: var(--gm-l-gold); background: var(--gm-s-gold); }
.gm-lk-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gm-sp-near); }
.gm-lk-card { box-sizing: border-box; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: var(--gm-sp-tight); padding: var(--gm-sp-near);
  border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m); background: var(--gm-s-card); color: var(--gm-c-main); font: var(--gm-fs-label) var(--gm-hei); cursor: pointer; }
@media (hover: hover) { .gm-lk-card:hover { border-color: var(--gm-l-edge); } }
.gm-lk-card.on { border-color: var(--gm-l-gold); background: var(--gm-s-gold); color: var(--gm-c-gold); }
.gm-lk-card.none { justify-content: center; min-height: 100%; color: var(--gm-c-dim); }
.gm-page.phone .gm-lk-card.none { min-height: max(100%, var(--gm-h-touch)); } /* alone (nothing worn yet): still a thumb high (点得到) */
.gm-lk-pic { width: 100%; aspect-ratio: 1; }
.gm-lk-card.none.on { color: var(--gm-c-gold); }
/* 技能 rows: the round icon, the skill, the worn look's own name on the right (gold; 「原版」 dim), a faint line between;
   the chosen one gold with a gold line on its left, like the phone menu's tabs. One a row, 48 high (a finger's 40 and more). */
.gm-lk-skills { display: flex; flex-direction: column; }
.gm-lk-skill { position: relative; box-sizing: border-box; min-width: 0; min-height: 48px; display: flex; align-items: center; gap: var(--gm-sp-inner); padding: var(--gm-sp-tight) var(--gm-sp-row);
  border: 0; border-bottom: 1px solid var(--gm-l-faint); background: none; color: var(--gm-c-main); font: var(--gm-fs-body) var(--gm-hei); text-align: left; cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
/* a long name (English, Japanese) wraps in its row between words (the row grows), nothing is cut off; a word is never
   broken in two (overflow-wrap: anywhere is only for what players type, 「换行」): no narrower than its longest word */
.gm-lk-skill > span:not(.gm-lk-ic) { flex: 1 1 auto; min-width: min-content; line-height: 1.3; }
.gm-lk-skill > b { flex: 0 1 auto; min-width: min-content; font-weight: 400; color: var(--gm-c-gold); line-height: 1.3; text-align: right; }
.gm-lk-skill > b.off { color: var(--gm-c-dim); }
@media (hover: hover) { .gm-lk-skill:hover { background: var(--gm-s-raise); } }
.gm-lk-ic { flex: none; width: 40px; height: 40px; border-radius: var(--gm-r-full); overflow: hidden; background: var(--gm-s-card); }
.gm-lk-ic > canvas { display: block; width: 100%; height: 100%; }
/* the stage (.gm-stage, 3:2) casting the worn look; its own name (题字 20, gold), the skill and 「使用中」 on its foot */
.gm-lk-stage { position: relative; flex: none; display: flex; flex-direction: column; }
.gm-lk-cap { position: absolute; left: var(--gm-sp-section); right: var(--gm-sp-section); bottom: var(--gm-sp-inner); display: flex; align-items: flex-end; justify-content: space-between; gap: var(--gm-sp-row); pointer-events: none; text-shadow: var(--gm-sh-text); }
.gm-lk-title { min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; column-gap: var(--gm-sp-row); } /* 窄的屏幕上技能名掉到下一行，外观名自己也能折行，不截断 */
.gm-lk-title > b { min-width: min-content; font: 700 var(--gm-fs-title) var(--gm-kai); color: var(--gm-c-gold); } /* wraps between words, never inside one */
.gm-lk-title > span { flex: none; font-size: var(--gm-fs-body); color: var(--gm-c-sub); white-space: nowrap; }
.gm-lk-on { flex: none; padding: 0 var(--gm-sp-tight); border-radius: var(--gm-r-s); background: var(--gm-s-gold); font-size: var(--gm-fs-label); font-style: normal; color: var(--gm-c-gold); white-space: nowrap; text-shadow: none; } /* 小牌子·金 (in use, 条与角标), like 会长 */
/* under it the shop's names (.gm-names); a phone's right column is too narrow for two (852 × 393: about 230), so one (a phone's one column too), and
   「使用中」 goes under a name too long to have it beside (never a word broken in two) */
.gm-page.phone .gm-lk-stage ~ .gm-names { grid-template-columns: minmax(0, 1fr); }
.gm-page.phone .gm-lk-stage ~ .gm-names > .gm-name { flex-wrap: wrap; }
.gm-page.phone .gm-lk-stage ~ .gm-names > .gm-name > span { flex: 1 1 auto; }

/* ---- The phone grip (src/client/play/touch.ts, docs/hud.md「手机」) ----------------------------------------
   Laid out for a phone on its side (about 852 × 393); everything sits in the corners, the middle is the fight. */
html.gm-touch-on #status:not(.reconnect) { display: none; } /* the admins' test line: on a phone the top line shows the ping */
/* 按下 = 指着 (用户 2026-10-15 选了草图 https://claude.ai/artifact/L4LVwg7C5t5QMi4m4kiuNc ④ 的 A): a phone has no pointer
   to hover, so the sheets' buttons go up the computer's hover step while the finger is on them and back when it lifts,
   over their own 快 transition. These rules 和上面每条 @media (hover: hover) 一一对应 (the same colour or the same edge
   line); what stays put under the mouse, a picked one or one that cannot be used, stays put under the finger. Hung on
   html.gm-press, which a phone (touchDevice()) has from the page's start (the lobby on) without touch-action: none,
   touch.ts adding it again in the game */
html.gm-press .gm-btn:active:not(:disabled), html.gm-press .gm-copy:not(.bad):active { background: var(--gm-s-hover); }
html.gm-press .gm-inv-no:active { background: var(--gm-s-hover); color: var(--gm-c-bad); }
html.gm-press .gm-btn.gold:active:not(:disabled), html.gm-press .gm-inv-yes:active, html.gm-press .gm-mk-row.on:active { background: var(--gm-s-gold-hover); }
.gm-dlg .gm-btn.danger:active:not(:disabled):is(html.gm-press *) { background: var(--gm-s-bad-hover); } /* written from .gm-dlg: the red surface's one home (colors-test) */
html.gm-press .gm-chip:not(.on):active { background: var(--gm-s-hover); color: var(--gm-c-main); }
html.gm-press .gm-seg button:not(.on):active { background: var(--gm-s-raise); color: var(--gm-c-main); }
html.gm-press .gm-item:active:not(:disabled), html.gm-press .gm-name-link:active, html.gm-press .gm-name:not(.on):active, html.gm-press .gm-who:not(.has):active,
html.gm-press .gm-set-row:active, html.gm-press .gm-cap:not(.on):not(.none):active, html.gm-press .gm-lk-skill:not(.on):active, html.gm-press .gm-mk-row:not(.on):active { background: var(--gm-s-raise); }
html.gm-press .gm-like:active:not(:disabled) { background: var(--gm-s-hover); }
/* the cards whose hover only lights the edge line: the same line under the finger (a picked or sold one keeps its gold) */
html.gm-press .gm-tier:not(.on):active, html.gm-press .gm-look-card:active, html.gm-press .gm-shop-item:active, html.gm-press .gm-sl.click:not(.on):not(.sold):active,
html.gm-press .gm-skill:not(.on):active, html.gm-press .gm-sell-row:active, html.gm-press .gm-lk-place:not(.on):active, html.gm-press .gm-lk-card:not(.on):active { border-color: var(--gm-l-edge); }
html.gm-touch-on, html.gm-touch-on body, html.gm-touch-on #stage { touch-action: none; overscroll-behavior: none; -webkit-touch-callout: none; -webkit-user-select: none; }
#ui > .gm-touch, .gm-touch { position: fixed; inset: 0; pointer-events: none; z-index: var(--gm-z-ui); font: var(--gm-fs-label) var(--gm-hei); color: var(--gm-c-main); } /* #ui > * would make the whole screen its own: only its keys take taps */
.gm-touch > * { pointer-events: auto; outline: none; }
.gm-touch [hidden] { display: none !important; }
:where(.gm-touch) button { all: unset; box-sizing: border-box; position: absolute; } /* no hand: a phone has no pointer to hover */
/* left: the joystick zone (the base comes where the thumb lands; at rest it waits at the lower left). Its size is measured
   (touch.ts sizeJoyZone): right to a 段 short of the leftmost right-hand key, up to a 段 over the keys round the resting joystick */
.gm-t-joyzone { position: absolute; left: 0; bottom: 0; width: var(--gm-t-joyw, 320px); height: var(--gm-t-joyh, 260px); } /* until measured (the first frame) */
/* our own drawing, not the original's steel ring: a faint ring, a dot at its middle and, pushed, a lit arc on it. At rest its
   left on the line 潮生岛 and 和平 start on; its size and its foot over the bottom edge × --gm-t-k (a small phone's keys shrink
   as one, touch.ts sets it from touch-layout.ts phoneKeyScale; JOY there: left 56, size 108, foot 49). The line itself
   × --gm-t-up: on a big screen the map key grows and the line moves out with it (8 + 40 + 8) */
.gm-t-joy { position: absolute; left: calc(env(safe-area-inset-left, 0px) + 56px * var(--gm-t-up, 1)); top: calc(100% - 157px * var(--gm-t-k, 1)); box-sizing: border-box; width: calc(108px * var(--gm-t-k, 1)); height: calc(108px * var(--gm-t-k, 1)); border-radius: var(--gm-r-full);
  border: 1.5px solid var(--gm-c-sub); background: radial-gradient(circle, color-mix(in srgb, var(--gm-s-sunk) 15%, transparent) 55%, color-mix(in srgb, var(--gm-s-sunk) 45%, transparent));
  box-shadow: var(--gm-sh-key); opacity: var(--gm-off); pointer-events: none; } /* JS puts its middle under the thumb; a key's edge and shadow (档位); at rest faint, the one see-through */
.gm-t-joy.act { opacity: 1; }
/* the middle: a small grey dot with the keys' dark edge (gold only says 「on」, docs/hud.md「手机的按键」); no ball, the thumb hid it */
.gm-t-joydot { position: absolute; left: calc(50% - 4px); top: calc(50% - 4px); width: 8px; height: 8px; border-radius: var(--gm-r-full); background: var(--gm-c-sub); box-shadow: var(--gm-sh-key); }
/* pushed: a 60° lit arc on the ring where the thumb points, turning smoothly with it (touch.ts turnArc; 用户 2026-10-14
   选了试玩页 https://claude.ai/artifact/PJAs7JNkrZgaW8h8DAxUNm 的「平滑亮弧」：「那就这个 先实装」) */
.gm-t-joyarc { position: absolute; inset: -1.5px; width: calc(100% + 3px); height: calc(100% + 3px); overflow: visible; opacity: 0; transition: opacity var(--gm-t-quick) var(--gm-ease-out); }
.gm-t-joyarc path { fill: none; stroke: var(--gm-c-bright); stroke-width: 4; stroke-linecap: round; }
.gm-t-joyarc.on { opacity: 1; filter: var(--gm-glow-icon); }
/* 跑 / 走 (a 40 round key like 翻页 and 锁人): the fourth on 随机 / 回城 / 药's arc, at its top (78 out from the resting
   joystick's middle, 78° above the right; touch.ts WALK_AT, placed by placeArc; 用户 2026-10-13 选 C, 草图 https://claude.ai/artifact/42yR4MnAYZeVVEYBsVaJee );
   the running one, the walking one lit, as big as 药 and the scrolls.
   The five on the arc (随机, 回城, 药, 跑 / 走, 宝宝) are one family (用户 2026-10-15): a 40 thumb room holding a 32 dark disc
   (面·卡, a key's edge and shadow: 宝宝's, 锁人's and a mate's inner disc), the thing centred on it (a drawn figure 24, the
   disc × 0.75; an item's picture 28), a count at the disc's lower right; on a big screen all × --gm-t-up (touch.ts places
   the room, QUICK_HIT × up, about its middle) */
.gm-t-walkkey { width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); padding: 0; border-radius: var(--gm-r-full); display: grid; place-items: center;
  color: var(--gm-c-sub); }
.gm-t-walkkey::before, .gm-t-quick .gm-t-q::before { content: ""; position: absolute; inset: calc(4px * var(--gm-t-up, 1)); border-radius: var(--gm-r-full); background: var(--gm-s-card); box-shadow: var(--gm-sh-key); }
/* 随机 / 回城 / 药, 跑 / 走, 宝宝 moved with the held joystick (touch.ts followArc): faint, the thumb is busy pushing */
.gm-t-q.follow, .gm-t-walkkey.follow, .gm-t-petkey.follow { opacity: var(--gm-off); }
.gm-t-walkkey.on { color: var(--gm-c-bright); filter: var(--gm-glow-icon); }
.gm-t-walkkey.on::before { background: var(--gm-s-gold); } /* lit like 宝宝 resting */
.gm-t-walkkey svg { position: relative; overflow: visible; width: calc(24px * var(--gm-t-up, 1)); height: calc(24px * var(--gm-t-up, 1)); fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
.gm-t-walkkey svg circle { fill: currentColor; stroke: none; }
.gm-t-walkkey svg .o path { stroke: var(--gm-s-sunk); stroke-width: 4.6; }
.gm-t-walkkey svg .o circle { fill: var(--gm-s-sunk); }
/* 宝宝 (attack <-> rest, = Ctrl+A): one step past 跑 / 走 round the same arc (78 out, 114° above the right: 48 from it,
   touch.ts PET_AT, placed by placeArc), only with a pet out. A round key like a teammate's: the 手机圆图标 of the skill the most hurt pet
   came from: 召唤骷髅's or 召唤神兽's, a tamed one 召唤骷髅's (a drawn skull until it loads), the ring the most hurt pet's HP; resting, the key glowing and a crescent moon on a small gold chip at the
   upper right (a mark: 「条与角标」, 用户 2026-10-17 照推荐); attack (the usual state) has no mark (用户 2026-10-13：位置选 B、样子要「攻击的小标」① 那种、要显示血,
   草图 https://claude.ai/artifact/83bd1pwJPvwvfszBzRWoxm ) */
.gm-t-petkey { width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); padding: 0; border-radius: var(--gm-r-full); display: grid; place-items: center;
  background: conic-gradient(var(--gm-c-blood) var(--hp, 100%), var(--gm-s-bad) 0);
  box-shadow: var(--gm-sh-key); }
.gm-t-petkey b { width: calc(32px * var(--gm-t-up, 1)); height: calc(32px * var(--gm-t-up, 1)); border-radius: var(--gm-r-full); display: grid; place-items: center; background: var(--gm-s-card); box-shadow: var(--gm-sh-badge); } /* the HP ring's inner edge: every HP bar has its 1-pixel dark edge (src/shared/space.ts BLOOD_W) */
.gm-t-petkey svg { width: calc(24px * var(--gm-t-up, 1)); height: calc(24px * var(--gm-t-up, 1)); fill: var(--gm-c-sub); stroke: var(--gm-s-sunk); stroke-width: 1.6; pointer-events: none; }
.gm-t-petkey .cut { fill: var(--gm-s-sunk); stroke: none; }
.gm-t-petkey .cut .t { fill: none; stroke: var(--gm-s-sunk); stroke-width: 1.6; }
/* the skill's 手机圆图标 (用户 2026-10-13 选「用技能圆图标」), scaled up inside a round window so its own bronze rim falls
   outside: the key keeps one ring, the HP (用户 2026-10-13：「不要和以前一样有金色圈」, 草图 https://claude.ai/artifact/W7CaUC3hUqhygV51ME8hXp);
   the window is the family's 32 disc */
.gm-t-petkey .pic { width: calc(32px * var(--gm-t-up, 1)); height: calc(32px * var(--gm-t-up, 1)); border-radius: var(--gm-r-full); overflow: hidden; background: var(--gm-s-sunk); box-shadow: var(--gm-sh-badge); }
.gm-t-petkey .pic canvas { display: block; width: 100%; height: 100%; transform: scale(1.13); }
.gm-t-petkey.on { filter: var(--gm-glow-icon); }
.gm-t-petkey.on b { background: var(--gm-s-gold); }
.gm-t-petkey.on b svg { fill: var(--gm-c-bright); }
.gm-t-petkey.on .gm-t-moon { position: absolute; right: -3px; top: -2px; width: 15px; height: 15px; border-radius: var(--gm-r-full); display: grid; place-items: center; background: var(--gm-c-bright); box-shadow: var(--gm-sh-badge); }
.gm-t-petkey.on .gm-t-moon svg { width: 15px; height: 15px; fill: var(--gm-s-sunk); stroke: none; }
/* right (the user's sketch): the main key at the corner, one big key above it, four small ones round it, 翻页 above.
   The pictures are the skills' round icons (手机圆图标): full circles with the keys' one dark edge and shadow
   (docs/hud.md「手机的按键」). Cooling: a dark sweep and the seconds; short of MP: deep blue; both inside the bronze rim. */
.gm-t-keys { position: absolute; inset: 0; pointer-events: none; }
.gm-t-keys > * { pointer-events: auto; }
.gm-touch .gm-t-skill, .gm-t-main { border-radius: var(--gm-r-full); }
.gm-t-main .gm-t-ic { position: absolute; inset: 0; border-radius: var(--gm-r-full); overflow: hidden; }
.gm-touch .gm-t-skill canvas, .gm-t-main canvas { display: block; width: 100%; height: 100%; border-radius: var(--gm-r-full); box-shadow: var(--gm-sh-key); }
.gm-t-cd { position: absolute; inset: var(--gm-rim-in); border-radius: var(--gm-r-full); background: conic-gradient(transparent calc(100% - var(--cd, 0%)), color-mix(in srgb, var(--gm-s-sunk) 60%, transparent) 0); /* 格子的意图语言: the light grows back clockwise from 12, the dark 0.6, as the desktop's clockSweep */ pointer-events: none; }
.gm-t-cd.gap { background: conic-gradient(transparent calc(100% - var(--cd, 0%)), color-mix(in srgb, var(--gm-s-sunk) calc(var(--gm-gap) * 100%), transparent) 0); } /* 施法间隔 (用户 2026-10-15「施法间隔 可以统一 我觉得写数字没啥用呢」) and the quick buttons' cooldown and recharge (用户 2026-10-16「淡淡的给一个冷却遮罩」): the same sweep, faint (--gm-gap), no seconds; a skill's own cooldown wins (touch.ts) */
.gm-t-cdn { position: absolute; inset: 0; display: grid; place-items: center; font: 700 var(--gm-fs-emph) var(--gm-num); color: var(--gm-c-main); pointer-events: none; scale: var(--pop, 1); /* 按了没用: jumps (touch.ts --pop) */
  text-shadow: var(--gm-sh-text); }
.gm-touch .gm-t-skill.nomp::after { content: ""; position: absolute; inset: var(--gm-rim-in); border-radius: var(--gm-r-full); background: var(--gm-tint-nomp); mix-blend-mode: multiply; pointer-events: none; } /* 蓝不够: deep blue as the desktop's slot (格子的意图语言), grey is only for 用完了 (用户 2026-10-13 同意) */
.gm-touch canvas.square { box-shadow: var(--gm-sh-key); } /* a skill without a round icon yet: its square art with a key's edge and shadow */
.gm-touch .gm-t-skill:has(> canvas.square) .gm-t-cd, .gm-touch .gm-t-skill:has(> canvas.square).nomp::after { inset: 0; } /* no bronze rim: the whole picture */
.gm-touch .gm-t-skill { width: 44px; height: 44px; }
/* 按了没用 (docs/hud.md「按了没用」): the key tips about its foot (touch.ts --tip, the desktop's denyTip); cooling, the seconds
   jump (--pop) and turn gold. Lifted under a dragged skill (.over): 1.14 and a deeper shadow. No motion asked: no tilt */
.gm-touch .gm-t-skill { rotate: var(--tip, 0deg); transform-origin: 50% 85%; transition: scale var(--gm-t-quick) var(--gm-ease-out); }
@media (prefers-reduced-motion: reduce) { .gm-touch .gm-t-skill { rotate: none; } }
.gm-t-cdn.pop { color: var(--gm-c-gold); }
/* 能续上 (魔法盾, 隐身 in their last seconds; 用户 2026-10-15：「金光呼吸是有困难的 你还有等级圈呢」): the picture inside the bronze rim
   breathes lighter and back on the 状态栏's beat (touch.ts --renew-at), the rim and its 三段环 stay; no motion asked: a still faint light */
.gm-t-cd::before { content: ""; position: absolute; inset: 0; border-radius: var(--gm-r-full); background: var(--gm-c-gold); mix-blend-mode: screen; opacity: 0; }
.gm-touch .gm-t-skill.renew .gm-t-cd::before { opacity: var(--gm-gap); } /* still: the faint strength (施法间隔's) */
@media (prefers-reduced-motion: no-preference) {
  .gm-touch .gm-t-skill.renew .gm-t-cd::before { animation: gm-renew var(--gm-t-breathe) ease-in-out var(--renew-at) infinite; /* --renew-at: touch.ts sets it before .renew */ }
}
@keyframes gm-renew { 0%, 100% { opacity: 0; } 50% { opacity: .4; } }
/* an empty key (用户 2026-10-16：「+号感觉可以不要了 因为有技能按钮」): not there while you play, not even to the thumb; with the
   技能 sheet open a dashed ring on a sunk ground, like the computer's empty slot, where a picked or dragged skill goes */
.gm-touch:not(.sheet) .gm-t-skill.empty { visibility: hidden; pointer-events: none; }
.gm-touch.sheet .gm-t-skill.empty { border-radius: var(--gm-r-full); background: var(--gm-s-sunk); outline: 2px dashed var(--gm-l-edge); outline-offset: -2px; }
.gm-touch .down { filter: brightness(1.4); }
/* 开着就换图 (用户 2026-10-15：「开启了 就那个刀会着火的样子才对」): a skill with its 手机圆图标·开着 (touch.ts .onart) is on by
   its picture alone, no gold ring that would join its bronze rim; only one without that picture yet glows */
.gm-touch .gm-t-skill.on:not(.onart) canvas { box-shadow: var(--gm-glow-on); }
/* the 三段环 (touch.ts ringOf) on the picture's own rim; the cooling sweep and the seconds inside it. A level gained: the
   new arc is drawn in light (快) and fades (停留), a faint ring spreads out (慢); at 3 the other two glow with it; 「n 级」 floats up
   over the key and goes (动效: in 快, 停留, out 慢). No motion asked: the rim just shows the new level. */
.gm-t-ring { position: absolute; overflow: visible; pointer-events: none; }
.gm-t-ring path, .gm-t-ring circle { fill: none; }
.gm-t-ring .d { stroke: var(--gm-ring-veil); }
.gm-t-ring .c { stroke: var(--gm-ring-cut); }
.gm-t-ring .f, .gm-t-ring .p { stroke: var(--gm-ring-light); opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
  .gm-t-ring .f.draw { animation: gm-ring-draw var(--gm-t-slow) var(--gm-ease-out) both, gm-ring-fade var(--gm-t-hold) var(--gm-ease-in) both; }
  .gm-t-ring .f.glow { animation: gm-ring-fade var(--gm-t-hold) var(--gm-ease-in) var(--gm-t-mid) both; }
  .gm-t-ring .p.spread { transform-box: fill-box; transform-origin: center; animation: gm-ring-spread var(--gm-t-slow) var(--gm-ease-out) var(--gm-t-mid) both; }
}
@keyframes gm-ring-draw { from { stroke-dasharray: 0 1000; } } /* to its own dash (touch.ts ringOf): drawn from the arc's start */
@keyframes gm-ring-fade { 0%, 40% { opacity: 1; } 100% { opacity: 0; } }
@keyframes gm-ring-spread { from { opacity: .5; transform: scale(1); } to { opacity: 0; transform: scale(1.18); } }
.gm-t-lvup { position: absolute; left: 50%; bottom: calc(100% + var(--gm-sp-row)); transform: translateX(-50%); white-space: nowrap; font-size: var(--gm-fs-label); color: var(--gm-c-bright);
  text-shadow: var(--gm-sh-text); pointer-events: none;
  animation: gm-lvup-in var(--gm-t-quick) var(--gm-ease-out) both, gm-out var(--gm-t-slow) var(--gm-ease-in) calc(var(--gm-t-quick) + var(--gm-t-hold)) forwards; }
@keyframes gm-lvup-in { from { opacity: 0; transform: translate(-50%, 4px); } }
/* 锁人: the one who hurt you most lately; the ring is his HP (血 --gm-c-blood, as every HP bar: 4 thick, src/shared/space.ts BLOOD_W.ring,
   its inner edge the disc's 1-pixel dark ring), his name under it in his head's colour */
.gm-t-lock { width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); border-radius: var(--gm-r-full); display: grid; place-items: center;
  background: conic-gradient(var(--gm-c-blood) var(--hp, 100%), var(--gm-s-bad) 0);
  box-shadow: var(--gm-sh-key); }
.gm-t-lock b { width: calc(32px * var(--gm-t-up, 1)); height: calc(32px * var(--gm-t-up, 1)); border-radius: var(--gm-r-full); display: grid; place-items: center; background: var(--gm-s-card); box-shadow: var(--gm-sh-badge); font: 700 var(--gm-fs-emph) var(--gm-kai); color: var(--gm-c-main); }
.gm-t-lock.lit { filter: var(--gm-glow-icon); }
/* his name left of the key, its right end against it (用户 2026-10-15 选了草图 https://claude.ai/artifact/L4LVwg7C5t5QMi4m4kiuNc ② 的 A):
   under it, it lay on the outer ring's keys; the line above the HUD (①) keeps clear of both (hud.ts phoneTop.keep) */
.gm-t-lockname { position: absolute; top: 50%; right: calc(100% + var(--gm-sp-tight)); transform: translateY(-50%); white-space: nowrap; font-size: var(--gm-fs-label);
  text-shadow: var(--gm-sh-text); pointer-events: none; } /* only says who: a tap on it goes to the key under it */
/* the party under the map key, its drawn left (the HP ring's) on the map drawing's drawn left (用户 2026-10-17; before 用户 2026-10-13 选 A, 草图 https://claude.ai/artifact/V6Xis2vhuuKWCqka3U1YyM;
   2026-10-15 草图 https://claude.ai/artifact/L4LVwg7C5t5QMi4m4kiuNc ⑧): round keys like 锁人's, 40, four a row and 8 apart, a full
   party (8) two rows; the name's first character, the ring his HP, the leader's crown (the desktop card's, 用户 2026-09-30) on its top's middle, the job on a chip at the upper right (a mark, 「条与角标」) */
.gm-touch > .gm-t-party { --gm-t-mapink: 5px; /* 对齐量画出来的边: how far the map drawing's ink starts right of the map key's left, before × up — the drawing
   is wide (横向, docs/ui-rules.md「图标」): its ink on its 30 box's left and right (touch.ts MAP_SVG, 用户 2026-10-17 选 A), so it is that box's left in the 40 key (.gm-t-map svg's left) */
  position: absolute; left: calc(env(safe-area-inset-left, 0px) + (8px + var(--gm-t-mapink)) * var(--gm-t-up, 1)); top: calc(56px * var(--gm-t-up, 1)); display: grid; grid-template-columns: repeat(4, calc(40px * var(--gm-t-up, 1))); gap: var(--gm-sp-row); pointer-events: none; }
.gm-t-mate { position: relative; width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); border-radius: var(--gm-r-full); display: grid; place-items: center; pointer-events: auto;
  background: conic-gradient(var(--gm-c-blood) var(--hp, 100%), var(--gm-s-bad) 0);
  box-shadow: var(--gm-sh-key); }
.gm-touch .gm-t-mate[hidden] { display: none; }
.gm-t-mate b { width: calc(32px * var(--gm-t-up, 1)); height: calc(32px * var(--gm-t-up, 1)); border-radius: var(--gm-r-full); display: grid; place-items: center; background: var(--gm-s-card); box-shadow: var(--gm-sh-badge); font: 700 var(--gm-fs-emph) var(--gm-kai); color: var(--gm-c-main); }
.gm-t-mate.far { opacity: var(--gm-off); } /* out of sight */
.gm-t-mate.lit { filter: var(--gm-glow-icon); } /* locked: as 锁人 */
.gm-t-mate .gm-t-crown { position: absolute; left: 50%; top: -6px; translate: -50% 0; width: 12px; height: 9px; background: var(--gm-c-gold); clip-path: polygon(0 100%, 0 25%, 25% 60%, 50% 0, 75% 60%, 100% 25%, 100% 100%); filter: var(--gm-sh-icon); }
.gm-t-mate .gm-t-job { position: absolute; right: -3px; top: -2px; min-width: 15px; height: 15px; padding: 0 var(--gm-sp-hair); box-sizing: border-box; border-radius: var(--gm-r-full); display: grid; place-items: center; background: var(--gm-s-raise); color: var(--gm-c-sub); font-size: var(--gm-fs-foot); box-shadow: var(--gm-sh-badge); }
/* your own party's ⋯ after the teammates (touch.ts more, docs/hud.md「手机」): a mate's round key without an HP ring, its menu 队伍 */
.gm-t-mate.gm-t-more { background: var(--gm-s-raise); }
.gm-t-mate.gm-t-more b { color: var(--gm-c-sub); }
.gm-t-mate.gm-t-more b::before { content: ""; width: calc(24px * var(--gm-t-up, 1)); height: calc(5px * var(--gm-t-up, 1)); /* ⋯ drawn: three dots in a 24 × 5 band (the face 32 × 0.75, 大小) */
  background: radial-gradient(circle closest-side, currentColor 85%, transparent 100%) 0 0 / calc(8px * var(--gm-t-up, 1)) 100% repeat-x; }
/* the gestures' picture, the first time on this phone (touch.ts gestureCard, docs/hud.md「手机」): no words; the fight a
   step darker, gold dashed rings on the keys, a tap's ripple, our own hand; a tap anywhere closes it. Nothing moves (动效：界面不动). */
.gm-gesture { position: absolute; inset: 0; z-index: var(--gm-z-ask); background: var(--gm-scrim-deep); pointer-events: auto; }
.gm-gesture > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.gm-gesture-ring { fill: none; stroke: var(--gm-l-gold); stroke-width: 2.5; stroke-dasharray: 6 5; filter: var(--gm-sh-icon); }
.gm-gesture-arrow { fill: var(--gm-c-gold); filter: var(--gm-sh-icon); }
.gm-gesture-path { fill: none; stroke: var(--gm-c-main); stroke-width: 2.5; stroke-dasharray: 3 4; stroke-linecap: round; }
.gm-gesture-dot { fill: color-mix(in srgb, var(--gm-c-gold) 35%, transparent); stroke: var(--gm-l-gold); stroke-width: 2; }
.gm-gesture-wave { fill: none; stroke: color-mix(in srgb, var(--gm-l-gold) 60%, transparent); stroke-width: 1.5; }
.gm-gesture-hand { fill: var(--gm-c-main); stroke: var(--gm-s-sunk); stroke-width: 1.2; stroke-linejoin: round; filter: var(--gm-sh-icon); }
/* 技能 (a long press on a skill key, a tap on an empty one; docs/hud.md「手机」): a sheet on the left reaching to the keys
   (touch.ts sets its width), the keys stay on the right and take taps and drags; the scene between is a little darker,
   a tap on it shuts the sheet; over one another in their order in .gm-touch (touch.ts), no z-index */
.gm-t-sheetscrim, .gm-t-sheet { display: none; }
.gm-touch.sheet .gm-t-sheetscrim { display: block; position: absolute; inset: 0; background: var(--gm-scrim); }
.gm-touch.sheet .gm-t-lock, .gm-touch.sheet .gm-t-main { pointer-events: none; }
.gm-touch.sheet .gm-t-sheet { display: block; position: absolute; left: 0; top: 0; bottom: 0; max-width: 560px; box-sizing: border-box; overflow: auto;
  padding: 0 var(--gm-sp-section) var(--gm-sp-inner) calc(env(safe-area-inset-left, 0px) + var(--gm-sp-section));
  background: var(--gm-s-sheet); border-right: 1px solid var(--gm-l-edge); }
.gm-t-sheet > .gm-ph { margin-bottom: var(--gm-sp-section); } /* the content 段 14 under the line, as every page */
/* 没装上 first, then 已在键上 (not dimmed: dark says 「can't」 on a phone; the group and where each is under its name say it; the open key's 「这一格」 lit) */
.gm-t-sheet > .gm-grp { margin: 0 0 var(--gm-sp-near); } /* a group title like every page's (15 / 500, its line, the count on the line's end): 段 14 under the last group, 近 6 over its own skills */
.gm-t-shlist + .gm-grp { margin-top: var(--gm-sp-section); }
/* six columns, fewer when the longest word on the sheet (--gm-t-skw, touch.ts fitSheet) and 紧 on each side is wider than a sixth */
.gm-t-shlist { display: grid; gap: var(--gm-sp-row); /* 常: things a thumb picks, 8 apart (点得到) */
  grid-template-columns: repeat(auto-fill, minmax(max(calc(var(--gm-t-skw, 0px) + 2 * var(--gm-sp-tight)), calc((100% - 5 * var(--gm-sp-row)) / 6 - 0.1px)), 1fr)); }
.gm-t-sheet .gm-t-sk { position: relative; display: grid; justify-items: center; align-content: start; gap: var(--gm-sp-tight); padding: var(--gm-sp-tight) 0; border-radius: var(--gm-r-s);
  font-size: var(--gm-fs-label); color: var(--gm-c-main); text-align: center; touch-action: none; }
.gm-t-sk.on { color: var(--gm-c-sub); }
.gm-t-sk small { margin-top: calc(-1 * var(--gm-sp-hair)); font-size: var(--gm-fs-foot); color: var(--gm-c-dim); }
/* a name breaks only between words, never inside one: the column is as wide as the longest word (docs/ui-rules.md「换行」);
   where it sits is a short word (touch.ts keyName). The hidden box that measures them: as wide as that word, nothing more */
.gm-t-skname, .gm-t-sk small { max-width: 100%; }
.gm-t-skname { word-break: keep-all; } /* a 漢字 name whole (日文「群体治癒術」 broke inside it): fitSheet measures it with this too, so the column is as wide as the whole name */
.gm-t-sheet .gm-t-shmeasure { position: absolute; left: 0; top: 0; display: block; width: min-content; padding: 0; visibility: hidden; pointer-events: none; }
.gm-t-sheet .gm-t-shmeasure > * { display: block; max-width: none; }
/* the picked skill (touch.ts this.picked): the whole card on 面·金 with a thin 线·金; its picture gets no gold ring (it would
   sit on the bronze rim and its 三段环) */
.gm-t-sk.sel { color: var(--gm-c-gold); } /* 面·金 and the bar: 选中的行 below */
.gm-t-sk.sel small { color: inherit; }
/* its 说明 under its row, the row's whole width (用户 2026-10-15：「这个自然展开 不要有框框住」): only lines, no box, ground or
   pointer; the rows under it move down. The lines are skill-tip.ts's, the desktop tooltip's */
.gm-t-shtip { grid-column: 1 / -1; padding: 0 var(--gm-sp-tight); font-size: var(--gm-fs-label); line-height: 1.5; color: var(--gm-c-sub); }
.gm-t-shtip p { margin: 0 0 var(--gm-sp-hair); }
.gm-t-shtip .name { font: 700 var(--gm-fs-phone) var(--gm-kai); color: var(--gm-c-bright); margin-bottom: var(--gm-sp-tight); }
.gm-t-shtip .main { color: var(--gm-c-main); }
.gm-t-shtip .bad, .gm-t-shtip .badSmall { color: var(--gm-c-bad); }
.gm-t-shtip .dimSmall { color: var(--gm-c-dim); }
.gm-t-shtip .badSmall, .gm-t-shtip .dimSmall { font-size: var(--gm-fs-foot); }
.gm-t-shtip .rule { display: block; height: var(--gm-sp-tight); }
.gm-t-skic, .gm-t-skic canvas { display: block; width: 40px; height: 40px; border-radius: var(--gm-r-full); }
.gm-t-sk .gm-t-skic { position: relative; } /* its 三段环 round it */
.gm-t-shnone { margin: 0; font-size: var(--gm-fs-body); color: var(--gm-c-dim); } /* empty: one grey line, 正文 13, where the content starts (四种状态) */
/* 手机换技能 (用户 2026-10-15：「点也可以 拖也可以 如何？」, 草图 https://claude.ai/artifact/42GMYWGmCMEkpRXxH5imnW 「手机换技能」):
   no key is 「being filled」. An active skill picked in the sheet (touch.ts .ready): every skill key lifts a little, 1.06 and a
   deeper shadow, and a tap puts it there. Dragging: the keys it can go on ringed faintly, the one under the thumb lifted
   1.14, the one it came off faint; the skill itself under the thumb. No gold ring on any (on the bronze rim it would join
   the 三段环) */
.gm-touch.ready .gm-t-skill { scale: 1.06; }
.gm-touch.ready .gm-t-skill canvas { box-shadow: var(--gm-sh-ready); }
.gm-touch .gm-t-skill.over { scale: 1.14; }
.gm-touch .gm-t-skill.over canvas { box-shadow: var(--gm-sh-lift); }
.gm-touch.dragging .gm-t-skill.drop:not(.over) canvas, .gm-touch.dragging .gm-t-skill.drop.empty:not(.over) { box-shadow: var(--gm-glow-can); }
.gm-touch .gm-t-skill.lift { opacity: var(--gm-off); }
.gm-t-ghost { position: absolute; width: 52px; height: 52px; pointer-events: none; transform: scale(1.1); filter: var(--gm-sh-icon); }
.gm-t-ghost canvas { display: block; width: 100%; height: 100%; border-radius: var(--gm-r-full); }
/* the − on every key with a skill (not a warrior's 攻击): a 15 badge (the size ladder's, as 宝宝's moon and a mate's job) at its upper left like an iPhone's app, 40 for the thumb (点得到); no shaking
   (用户 2026-10-13：「学习iphone里面删除app一样」) */
.gm-t-minus { position: absolute; left: -15px; top: -15px; width: 40px; height: 40px; display: grid; place-items: center; }
.gm-t-minus::before, .gm-t-minus::after { content: ""; grid-area: 1 / 1; }
.gm-t-minus::before { width: 15px; height: 15px; border-radius: var(--gm-r-full); background: var(--gm-s-raise); box-shadow: var(--gm-sh-badge); }
.gm-t-minus::after { width: 8px; height: 2px; border-radius: var(--gm-r-s); background: var(--gm-c-main); }
/* 随机, 回城, 药: on an arc up and right of the resting joystick (touch.ts QUICK_ARC; 用户 2026-10-13 选 A), the left thumb's,
   the same for every job. Each on the arc family's 32 disc (跑 / 走 above), the count at the disc's lower right; just used: a
   dark sweep over the disc; none left (or not here): grey */
.gm-touch > .gm-t-quick { position: absolute; left: env(safe-area-inset-left, 0px); bottom: 0; width: 0; height: 0; pointer-events: none; }
.gm-t-quick .gm-t-q { position: absolute; box-sizing: border-box; width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); display: grid; place-items: center; pointer-events: auto; border-radius: var(--gm-r-full); } /* the thumb's room 40 × up and round (the five on the arc are 36° apart) */
.gm-t-quick .gm-t-q.off { filter: grayscale(1) brightness(var(--gm-grey)); }
.gm-t-quick .gm-t-q.off b { color: var(--gm-c-dim); }
.gm-t-quick .gm-t-cd { inset: calc(4px * var(--gm-t-up, 1)); } /* the disc, not a ring inside it */
.gm-t-quick b { position: absolute; right: 1px; bottom: 0; font: 700 var(--gm-fs-label) var(--gm-num); color: var(--gm-c-main);
  text-shadow: var(--gm-sh-text); }
.gm-t-quick canvas { position: relative; display: block; width: calc(28px * var(--gm-t-up, 1)); height: calc(28px * var(--gm-t-up, 1)); object-fit: contain; filter: var(--gm-sh-icon); } /* an item's picture on the disc (its own clear margin keeps it inside the round) */
/* top: the map key (Tab, no ground under it), the scene and the attack mode, the level after it; battery · time · ping; the chat key, the menu key.
   The three keys 8 in from the safe area's edges, 40 for the thumb, the drawing in a 30 box at its middle (the key × 0.75);
   on a big screen the keys, their places and the boxes × --gm-t-up (the words never grow) */
.gm-t-map { left: calc(env(safe-area-inset-left, 0px) + 8px * var(--gm-t-up, 1)); top: calc(6px * var(--gm-t-up, 1)); width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); } /* 8 from 和平 (点得到), like the chat key */
.gm-t-map svg { position: absolute; left: calc(5px * var(--gm-t-up, 1)); top: calc(5px * var(--gm-t-up, 1)); width: calc(30px * var(--gm-t-up, 1)); height: calc(30px * var(--gm-t-up, 1)); filter: var(--gm-sh-icon); }
.gm-t-map svg * { stroke-linejoin: round; }
/* shut: a dim grey-brown map, the pin dark */
.gm-t-map svg .a { fill: var(--gm-s-raise); }
.gm-t-map svg .b { fill: var(--gm-s-hover); }
.gm-t-map svg .r { fill: none; stroke: none; stroke-width: .8; stroke-dasharray: 1.2 1; }
.gm-t-map svg .k { fill: none; stroke: var(--gm-c-off); stroke-width: .9; }
.gm-t-map svg .o { fill: none; stroke: var(--gm-c-dim); stroke-width: 1.3; }
.gm-t-map svg .pin { fill: color-mix(in srgb, var(--gm-c-blood) 50%, var(--gm-s-raise)); stroke: var(--gm-s-sunk); stroke-width: .7; }
.gm-t-map svg .h { fill: var(--gm-s-raise); }
/* open (近看 / 全貌): gold paper, a red pin, a route, a ring of light */
.gm-t-map.lit svg { filter: var(--gm-glow-icon) var(--gm-sh-icon); }
.gm-t-map.lit svg .a { fill: color-mix(in srgb, var(--gm-c-gold) 75%, var(--gm-l-edge)); }
.gm-t-map.lit svg .b { fill: var(--gm-c-bright); }
.gm-t-map.lit svg .r { stroke: color-mix(in srgb, var(--gm-c-blood) 45%, var(--gm-l-edge)); }
.gm-t-map.lit svg .k { stroke: var(--gm-l-edge); }
.gm-t-map.lit svg .o { stroke: var(--gm-c-main); stroke-width: 1.2; }
.gm-t-map.lit svg .pin { fill: var(--gm-c-bad); stroke: color-mix(in srgb, var(--gm-c-blood) 50%, var(--gm-s-sunk)); }
.gm-t-map.lit svg .h { fill: var(--gm-c-main); }
.gm-t-mapname { position: absolute; left: calc(env(safe-area-inset-left, 0px) + 56px * var(--gm-t-up, 1)); top: calc(26px * var(--gm-t-up, 1) - 17px); text-shadow: var(--gm-sh-text); pointer-events: none; white-space: nowrap; }
.gm-t-mapname small { margin-left: var(--gm-sp-row); font-size: var(--gm-fs-label); color: var(--gm-c-sub); font-variant-numeric: tabular-nums; } /* the character's cell, right of the map's name */
.gm-t-base { display: inline-block; width: 0; height: 0; } /* empty, standing on its line: its foot is the line's baseline (touch.ts gainsBox) */
.gm-t-modeline { position: absolute; left: calc(env(safe-area-inset-left, 0px) + 56px * var(--gm-t-up, 1)); top: calc(26px * var(--gm-t-up, 1) + 1px); display: flex; align-items: baseline; white-space: nowrap; pointer-events: none; } /* 和平 and the level share a baseline whatever fonts the phone has */
#ui .gm-t-mode { position: relative; pointer-events: auto; font: 700 var(--gm-fs-emph) var(--gm-kai); text-shadow: var(--gm-sh-text); } /* #ui: over a button's own font (正文) */
.gm-t-mode::after { content: ""; position: absolute; left: 50%; bottom: 0; width: max(100%, var(--gm-h-touch)); height: var(--gm-h-touch); transform: translateX(-50%); } /* 和平 is two characters: the thumb's 40 × 40 is a band nobody sees (点得到), on its middle across, rising from its foot over the map name so it stays 8 clear of the mates under it (.gm-t-party) */
/* battery · time · ping: top right beside the menu key, on the map name's line (the top middle is the offers' slot and the ① line's);
   its right end 152 from the edge, left of 技能 · 聊天 · 菜单 (8 + 40 + 8 + 40 + 8 + 40 + 8) and its middle on the keys' (26) × --gm-t-up with the keys */
.gm-t-status { position: absolute; right: calc(env(safe-area-inset-right, 0px) + 152px * var(--gm-t-up, 1)); top: calc(26px * var(--gm-t-up, 1) - 22px); height: 44px; display: flex; gap: var(--gm-sp-row); align-items: center; font-size: var(--gm-fs-label); font-variant-numeric: tabular-nums; text-shadow: var(--gm-sh-text); pointer-events: none; } /* one line, its middle on the chat and menu keys' middle (用户 2026-10-13) */
.gm-t-ping { color: var(--gm-c-good); }
.gm-t-ping.slow { color: var(--gm-c-sub); } /* not gold: gold is 「on」 on a phone, and there is no warning yellow (docs/colors.md) */
.gm-t-ping.bad { color: var(--gm-c-bad); }
/* a battery, not a pill: a 1 outline, the charge (--b, touch.ts) 1 inside it, a 2 × 4 nub on the right */
.gm-t-bat { position: relative; box-sizing: border-box; width: 20px; height: 10px; border: 1px solid var(--gm-c-main); border-radius: var(--gm-r-s);
  background: linear-gradient(90deg, var(--gm-c-main) var(--b, 100%), transparent var(--b, 100%)) 1px 1px / calc(100% - 2px) calc(100% - 2px) no-repeat; }
.gm-t-bat::after { content: ""; position: absolute; left: calc(100% + 1px); top: calc(50% - 2px); width: 2px; height: 4px; background: var(--gm-c-main); }
.gm-t-menu { right: calc(env(safe-area-inset-right, 0px) + 8px * var(--gm-t-up, 1)); top: calc(6px * var(--gm-t-up, 1)); width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); } /* 8 from the chat key (点得到), 8 from the edge like the map key */
.gm-t-menu svg.bars { position: absolute; left: calc(5px * var(--gm-t-up, 1)); top: calc(5px * var(--gm-t-up, 1)); width: calc(30px * var(--gm-t-up, 1)); height: calc(30px * var(--gm-t-up, 1)); fill: var(--gm-c-sub); stroke: var(--gm-s-sunk); stroke-width: 1.6; paint-order: stroke; overflow: visible; filter: var(--gm-sh-icon); } /* grey like 跑: gold says 「on」; the bars fill the box, their outline may spill past it */
.gm-t-menu.lit svg.bars { fill: var(--gm-c-bright); filter: var(--gm-glow-icon) var(--gm-sh-icon); } /* the menu open: gold and glowing like the map, 技能 and chat keys open (docs/ui-rules.md「图标」, 用户 2026-10-17 看草图第七张：都照推荐) */
/* the chat key: two bubbles drawn like the map key; gold while the chat is open, a red dot for a 私聊 not seen yet */
.gm-t-chat { right: calc(env(safe-area-inset-right, 0px) + 56px * var(--gm-t-up, 1)); top: calc(6px * var(--gm-t-up, 1)); width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); }
.gm-t-chat svg { position: absolute; left: calc(5px * var(--gm-t-up, 1)); top: calc(5px * var(--gm-t-up, 1)); width: calc(30px * var(--gm-t-up, 1)); height: calc(30px * var(--gm-t-up, 1)); filter: var(--gm-sh-icon); }
.gm-t-chat svg .a { fill: var(--gm-s-raise); }
.gm-t-chat svg .b { fill: var(--gm-s-hover); }
.gm-t-chat svg .o { fill: none; stroke: var(--gm-c-dim); stroke-width: 1.3; stroke-linejoin: round; }
.gm-t-chat svg .d { fill: var(--gm-c-dim); }
.gm-t-chat svg .pin { display: none; fill: var(--gm-c-bad); stroke: var(--gm-s-sunk); stroke-width: .8; }
.gm-t-chat.dot svg .pin { display: inline; }
.gm-t-chat.lit svg { filter: var(--gm-glow-icon) var(--gm-sh-icon); }
.gm-t-chat.lit svg .a { fill: color-mix(in srgb, var(--gm-c-gold) 75%, var(--gm-l-edge)); }
.gm-t-chat.lit svg .b { fill: var(--gm-c-bright); }
.gm-t-chat.lit svg .o { stroke: var(--gm-c-main); stroke-width: 1.2; }
.gm-t-chat.lit svg .d { fill: var(--gm-c-ink); }
.gm-t-skills { right: calc(env(safe-area-inset-right, 0px) + 104px * var(--gm-t-up, 1)); top: calc(6px * var(--gm-t-up, 1)); width: calc(40px * var(--gm-t-up, 1)); height: calc(40px * var(--gm-t-up, 1)); } /* 8 left of the chat key: 技能 · 聊天 · 菜单 */
.gm-t-skills svg { position: absolute; left: calc(5px * var(--gm-t-up, 1)); top: calc(5px * var(--gm-t-up, 1)); width: calc(30px * var(--gm-t-up, 1)); height: calc(30px * var(--gm-t-up, 1)); filter: var(--gm-sh-icon); }
.gm-t-skills svg .a { fill: var(--gm-s-raise); }
.gm-t-skills svg .b { fill: var(--gm-s-hover); }
.gm-t-skills svg .o { fill: none; stroke: var(--gm-c-dim); stroke-width: 1.3; stroke-linejoin: round; }
.gm-t-skills svg .k { fill: none; stroke: var(--gm-c-dim); stroke-width: 1; stroke-linecap: round; }
.gm-t-skills svg .d { fill: var(--gm-c-dim); }
.gm-t-skills.lit svg { filter: var(--gm-glow-icon) var(--gm-sh-icon); }
.gm-t-skills.lit svg .a { fill: color-mix(in srgb, var(--gm-c-gold) 75%, var(--gm-l-edge)); }
.gm-t-skills.lit svg .b { fill: var(--gm-c-bright); }
.gm-t-skills.lit svg .o { stroke: var(--gm-c-main); stroke-width: 1.2; }
.gm-t-skills.lit svg .k { stroke: var(--gm-c-ink); }
.gm-t-skills.lit svg .d { fill: var(--gm-c-ink); }
/* 有新版本 · 刷新 on a phone (index.html #update; play-page.ts puts 刷新 in .gm-update-go): the words 字·主 so they read over
   the scene, 刷新 gold and underlined: the part that does something reads as a link */
html.gm-touch-on #update { color: var(--gm-c-main); }
html.gm-touch-on .gm-update-go { color: var(--gm-c-gold); text-decoration: underline; text-underline-offset: 2px; }
/* the safe area's edges, measured (touch.ts chatBox) */
.gm-touch > .gm-t-safe { position: absolute; left: env(safe-area-inset-left, 0px); right: env(safe-area-inset-right, 0px); bottom: env(safe-area-inset-bottom, 0px); height: 0; pointer-events: none; } /* the safe area's left, right and bottom edges, measured (touch.ts chatBox, bottomInset) */
/* the chat open (its sheet, drawn by the HUD): the keys put away, like the menu's */
html.gm-chat-on .gm-touch > :not(.gm-t-safe) { display: none; } /* the safe area's edges stay measurable (touch.ts chatBox) */
/* the level on the attack mode's line, starting under the coordinates, written like them (用户 2026-10-13：「lv和上方的坐标没有对齐」
   「字体和颜色也需要向上方的坐标对齐」; touch.ts puts it there) */
.gm-t-level { font-size: var(--gm-fs-label); color: var(--gm-c-sub); font-variant-numeric: tabular-nums; text-shadow: var(--gm-sh-text); pointer-events: none; white-space: nowrap; }
/* held upright, from the login on (src/client/play/rotate.ts): turn it sideways, a phone turning a quarter round over it;
   under it how to unlock a locked screen (iPhone: Apple's button, as it looks on → off) */
.gm-rotate { display: none; position: fixed; inset: 0; box-sizing: border-box; padding: 0 var(--gm-sp-page); background: var(--gm-s-sunk); color: var(--gm-c-main); flex-direction: column; align-items: center; justify-content: center; gap: var(--gm-sp-section); text-align: center; }
.gm-rotate b { font: 700 var(--gm-fs-title) var(--gm-kai); color: var(--gm-c-gold); } /* the screen's title: a page's name, 楷书 20 金·题 */
.gm-rotate p { margin: 0; max-width: 300px; font-size: var(--gm-fs-body); line-height: 1.8; color: var(--gm-c-sub); }
.gm-rotate-locks { display: flex; align-items: center; gap: var(--gm-sp-section); }
.gm-rotate-locks svg { width: 60px; height: 60px; }
.gm-rotate-locks svg.to { width: 24px; height: 12px; fill: none; stroke: var(--gm-c-dim); stroke-width: 1.5; }
.gm-rotate-locks .ring, .gm-rotate-locks .sh { fill: none; stroke-width: 3; stroke-linecap: round; }
.gm-rotate-locks .sh { stroke-width: 2.6; }
.gm-rotate-locks .lk-on .bg { fill: #fff; } /* 材质：苹果控制中心「屏幕方向锁定」开着的样子（白底红锁） */
.gm-rotate-locks .lk-on .ring, .gm-rotate-locks .lk-on .sh { stroke: #ff3b30; } /* 材质：同上 */
.gm-rotate-locks .lk-on .head, .gm-rotate-locks .lk-on .lk { fill: #ff3b30; } /* 材质：同上 */
.gm-rotate-locks .lk-off .bg { fill: rgba(120, 120, 120, .45); } /* 材质：关着的样子（灰底、灰圈、白色开锁） */
.gm-rotate-locks .lk-off .ring { stroke: #a5a5a5; } /* 材质：同上 */
.gm-rotate-locks .lk-off .head { fill: #a5a5a5; } /* 材质：同上 */
.gm-rotate-locks .lk-off .sh { stroke: #fff; } /* 材质：同上 */
.gm-rotate-locks .lk-off .lk { fill: #fff; } /* 材质：同上 */
.gm-rotate-turn { width: 44px; height: 44px; }
.gm-rotate-turn rect, .gm-rotate-turn path { fill: none; stroke: var(--gm-c-gold); stroke-width: 2; stroke-linecap: round; }
/* upright, a quarter round to sideways, a rest, back: one turn per two breaths (例外，docs/ui-rules.md「动效」) */
@media (prefers-reduced-motion: no-preference) { .gm-rotate-turn g { transform-origin: 22px 22px; animation: gm-turn calc(var(--gm-t-breathe) * 2) var(--gm-ease-out) infinite; } }
@keyframes gm-turn { 0%, 25% { transform: rotate(0); } 50%, 85% { transform: rotate(-90deg); } 100% { transform: rotate(0); } }
@media (orientation: portrait) { .gm-rotate { display: flex; z-index: var(--gm-z-alarm); } }
/* ---- The phone's menu (src/client/play/phone-menu.ts, docs/hud.md「手机的菜单」) -----------------------------------
   A sheet on the right; the strip on the left stays the fight (a tap there closes it). The HUD draws the picked
   thing's card into the detail column's top part, on its canvas over the DOM (no tooltips on a phone). */
html.gm-pm-on .gm-touch > :not(.gm-t-safe) { display: none; } /* the safe area's edges stay measurable (touch.ts chatBox) */
.gm-pm { position: fixed; inset: 0; z-index: var(--gm-z-ui); font: var(--gm-fs-body) var(--gm-hei); color: var(--gm-c-main); -webkit-user-select: none; user-select: none; }
.gm-pm[hidden] { display: none; }
.gm-pm-scrim { position: absolute; left: 0; top: 0; bottom: 0; width: calc(env(safe-area-inset-left, 0px) + var(--gm-strip)); background: var(--gm-scrim); } /* the fight left showing, a little darker: a tap there only closes the sheet */
.gm-pm-sheet { position: absolute; top: 0; bottom: 0; right: 0; left: calc(env(safe-area-inset-left, 0px) + var(--gm-strip)); display: flex; border-left: 1px solid var(--gm-l-edge); background: var(--gm-s-sheet); } /* opaque (面·页): the fight's names never show through the sheet or its tabs */
.gm-pm-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* the tabs down the sheet's left edge (用户 2026-10-13): 楷书, the lit one gold with a gold line on its left */
.gm-pm-tabs { flex: none; box-sizing: border-box; width: var(--gm-tabcol); display: flex; flex-direction: column; gap: var(--gm-tabgap); background: var(--gm-s-sheet); border-right: 1px solid var(--gm-l-line); }
/* each tab as tall as the head beside it (its line not counted), the first level with it: 「角色」 on the page name's middle line; PHONE_TABCOL.pitch apart (phone-menu.ts fitPitch, --gm-tabh / --gm-tabgap) */
#ui .gm-pm-tabs button { flex: 0 0 var(--gm-tabh); min-height: 0; padding: 0 var(--gm-sp-tight); white-space: nowrap; border: 0; background: none; color: var(--gm-c-dim); font: 700 var(--gm-tabfont)/normal var(--gm-kai); position: relative; } /* /normal: the word a line of the face's own height in the tab's exact middle (a line-height under it is rounded, half a pixel off) */
#ui .gm-pm-tabs button.on { color: var(--gm-c-gold); } /* as strong as the tab's own rule above (#ui); 面·金 and the bar: 选中的行 at the end */
.gm-pm-tabs button[hidden] { display: none; }
.gm-pm-body { flex: 1; min-height: 0; position: relative; background: var(--gm-s-sheet); }
.gm-pm-body.dom { padding: var(--gm-sp-section) calc(env(safe-area-inset-right, 0px) + var(--gm-sp-section)) 0 var(--gm-sp-section); }
.gm-pm-own { position: absolute; inset: 0; display: flex; }
/* 查看装备: only the 角色 page's left half, so a narrow sheet; the scrim is all the rest (a tap there closes it) */
.gm-pm.narrow .gm-pm-sheet { left: auto; width: calc(env(safe-area-inset-right, 0px) + var(--gm-equip-w)); }
.gm-pm.narrow .gm-pm-scrim { width: auto; right: 0; }
.gm-pm-insp { position: absolute; inset: 0; display: flex; flex-direction: column; padding: var(--gm-sp-section) calc(env(safe-area-inset-right, 0px) + var(--gm-sp-section)) var(--gm-sp-section) var(--gm-sp-section); overflow-y: auto; }
.gm-pm-insp[hidden] { display: none; }
/* a face-to-face trade: their items not looked at yet dimmed with a white dot (as the desktop's window); your side under
   theirs; 取消交易 and 接受 at the foot of the right column */
.gm-pm .gm-cell-it.unseen::before { content: ""; position: absolute; inset: 0; background: color-mix(in srgb, var(--gm-s-sunk) 55%, transparent); pointer-events: none; }
/* its white dot: a mark at the upper right (角标), over the veil (drawn after it), with the badges' dark ring */
.gm-pm .gm-cell-it.unseen::after { content: ""; position: absolute; top: var(--gm-sp-tight); right: var(--gm-sp-tight); width: 6px; height: 6px; border-radius: var(--gm-r-full); background: var(--gm-c-main); box-shadow: var(--gm-sh-badge); pointer-events: none; }
.gm-pm-mine { margin-top: var(--gm-sp-section); }
.gm-pm-ok { color: var(--gm-c-good); white-space: nowrap; } /* 「已接受」 one block: a narrow line folds before it, never inside it */
.gm-pm-goldname { font-size: var(--gm-fs-label); color: var(--gm-c-sub); }
.gm-pm-traderow { margin-top: var(--gm-sp-row); flex-wrap: wrap; }
.gm-pm-goldpair { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--gm-sp-tight); } /* 紧 4: 「身上 X」 and its box are one thing's two halves; too wide beside 「金币」 (its basis is its whole width), the pair goes under it whole; still too wide (English on a 667), the box goes under 「身上 X」, 4 apart, the whole width */
.gm-pm-goldpair > .gm-pm-goldin { flex: 1 1 84px; width: 84px; min-width: 84px; } /* 84 shows 「1,500,000」 whole, never narrower: no room beside 「身上 X」 (never broken), it takes the line under it */
/* 「身上 X」 between 金币 and the box: the gold you can put in (用户 2026-10-15 选 C), 字·次 12, the sum gold 13, never broken */
.gm-pm-carried { flex: none; white-space: nowrap; font-size: var(--gm-fs-label); color: var(--gm-c-sub); }
.gm-pm-carried b { font-size: var(--gm-fs-body); font-weight: 500; color: var(--gm-c-gold); font-variant-numeric: tabular-nums; }
.gm-pm .gm-say:empty { display: none; } /* the line saying a box was filled wrong takes no room until it says something */
.gm-pm-tradefoot { flex: none; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--gm-sp-tight) var(--gm-sp-row); margin-top: auto; padding-top: var(--gm-sp-inner); }
.gm-pm-tradefoot > .gm-pm-none { flex: 0 0 100%; padding: 0; text-align: right; } /* 「还有 n 件没看」 a line of its own over the buttons, on the right; none, no line */
/* an NPC's talk: what it says, then its options a whole row each, a thumb high (docs/ui-rules.md「点得到」) */
.gm-pm-talk { position: absolute; inset: 0; overflow-y: auto; padding: var(--gm-sp-section) calc(env(safe-area-inset-right, 0px) + var(--gm-sp-section)) var(--gm-sp-section) var(--gm-sp-section); }
.gm-pm-talk[hidden] { display: none; }
.gm-pm-say { margin: 0 0 var(--gm-sp-section); line-height: 1.6; } /* as the computer's .gm-talk */
.gm-pm-opts { display: flex; flex-direction: column; gap: var(--gm-sp-tight); }
/* an NPC's options: a list's rows one taps, the computer's (hud.ts buildDialog) a thumb high: 面·凸, 小 3, words 13 内 10 from the left (选择) */
.gm-pm-opt { min-height: var(--gm-h-touch); padding: 0 var(--gm-sp-inner); border: 0; border-radius: var(--gm-r-s); background: var(--gm-s-raise); color: var(--gm-c-main); font: var(--gm-fs-emph) var(--gm-hei); text-align: left; transition: background var(--gm-t-quick) var(--gm-ease-out); }
html.gm-press .gm-pm-opt:active { background: var(--gm-s-hover); } /* 按下 = 指着: 凸 → 悬停 */
.gm-pm-own[hidden] { display: none; }
.gm-pm-left { flex: 0 0 auto; box-sizing: border-box; width: calc(var(--gm-equip-w) + 2 * var(--gm-sp-section)); overflow-y: auto; scrollbar-width: none; display: flex; flex-direction: column; padding: var(--gm-sp-section); border-right: 1px solid var(--gm-l-line); }
.gm-pm-right { flex: 1; min-width: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; display: flex; flex-direction: column; padding: var(--gm-sp-section) calc(env(safe-area-inset-right, 0px) + var(--gm-sp-section)) var(--gm-sp-section) var(--gm-sp-section); }
.gm-pm-list { flex: none; min-width: 0; display: flex; flex-direction: column; } /* the bag's block in its middle (.gm-pm-block) */
.gm-pm-under { flex: 1; min-height: 0; margin-top: var(--gm-sp-section); display: flex; flex-direction: column; }
.gm-pm-card { flex: 1; min-height: 0; } /* the HUD draws the picked thing's card here, without its frame */
.gm-pm-acts { flex: none; display: flex; flex-wrap: wrap; gap: var(--gm-sp-row); margin-top: auto; padding-top: var(--gm-sp-inner); }
.gm-pm-acts:empty { display: none; }
.gm-pm-lbl { display: flex; justify-content: space-between; margin-bottom: var(--gm-sp-near); font-size: var(--gm-fs-label); color: var(--gm-c-sub); }
.gm-pm-lbl b { color: var(--gm-c-main); font-weight: 500; font-variant-numeric: tabular-nums; }
/* the bag's head: 「背包」, never broken */
.gm-pm-baglbl { column-gap: var(--gm-sp-row); } /* 「背包」 alone: the money is on the head (面板统一版式) */
.gm-pm-lblname { flex: none; white-space: nowrap; }
.gm-pm-grid { position: relative; background-repeat: repeat; }
.gm-pm-grid .gm-cell-it { position: absolute; }
.gm-pm .gm-cell-it.sel { outline: var(--gm-sel-w) solid var(--gm-l-gold); outline-offset: calc(-1 * var(--gm-sel-w)); } /* drawn last in the grid, so its ring is on top */
/* the worn one the picked thing is compared with (compare.ts versus): 线·金 dashed, outside the cell, so the quality
   colour of its own cell does not swallow it (用户 2026-10-16: the faint ring inside could not be seen on a phone) */
.gm-pm .gm-cell-it.vs { outline: var(--gm-sel-w) dashed var(--gm-l-gold); outline-offset: var(--gm-sel-w); }
.gm-pm-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gm-sp-row) var(--gm-sp-tight); }
.gm-pm-stats div { display: flex; flex-direction: column; align-items: center; gap: var(--gm-sp-hair); }
.gm-pm-stats span { font-size: var(--gm-fs-foot); color: var(--gm-c-dim); white-space: nowrap; }
.gm-pm-stats b { font: 700 var(--gm-statfont) var(--gm-num); font-variant-numeric: tabular-nums; white-space: nowrap; } /* 角色页属性行 is 跳字, as the desktop's (字体规划) */
/* the desktop's gear places (EQUIP_LAYOUT × 0.8), in the column's middle */
.gm-pm-gear { position: relative; flex: none; align-self: center; }
.gm-pm-gear[hidden] { display: none; }
.gm-pm-gear > * { position: absolute; }
.gm-pm-empty { background-size: 100% 100%; }
/* the flask's slot (角色 page only, phone-menu.ts FLASK_AT): round, the fight's 药 key — the dark disc with a key's edge and shadow, the picture on it */
.gm-pm-flask { box-sizing: border-box; display: grid; place-items: center; border-radius: var(--gm-r-full); background: var(--gm-s-card); box-shadow: var(--gm-sh-key); }
.gm-pm-flask .gm-cell-it canvas { filter: var(--gm-sh-icon); }
.gm-pm-flask.sel { outline: var(--gm-sel-w) solid var(--gm-l-gold); }
.gm-pm-flask.vs { outline: var(--gm-sel-w) dashed var(--gm-l-gold); outline-offset: var(--gm-sel-w); }
/* An NPC's window on a phone (phone-menu.ts): the NPC's head in place of the tabs, its things left, the bag right. */
.gm-pm-npchead { flex: none; display: flex; align-items: center; gap: var(--gm-sp-row); min-height: calc(var(--gm-h-head) + 1px); box-sizing: border-box; padding: var(--gm-sp-tight) calc(env(safe-area-inset-right, 0px) + var(--gm-sp-section)) var(--gm-sp-tight) var(--gm-sp-section); background: var(--gm-s-sheet); border-bottom: 1px solid var(--gm-l-line); } /* its line 46 under the top as every page head's (--gm-h-head, + the line itself: border-box); its state ends where the content does: 页边 14 and the safe area */
.gm-pm-npchead[hidden], .gm-pm-tabs[hidden], .gm-pm-npc[hidden] { display: none; }
.gm-pm-npchead > b { flex: none; font: 700 var(--gm-fs-title)/normal var(--gm-kai); color: var(--gm-c-gold); } /* /normal as the tabs': the name in the head's exact middle, level with the first tab's (对齐和量位置) */
.gm-pm-npchead > span { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-pm-headsay { flex: 1; min-width: 0; margin: 0; font: var(--gm-fs-body)/1.5 var(--gm-hei); color: var(--gm-c-sub); } /* a sentence: 正文 13, as the NPC dialog's words (字体规划: 题字 only for names) */
.gm-pm-npc { position: absolute; inset: 0; display: flex; }
.gm-pm-col { flex: 1 1 0; min-width: 0; box-sizing: border-box; position: relative; display: flex; flex-direction: column; padding: var(--gm-sp-section); overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none; } /* too tall: it scrolls, the cells keep their size (phone-cell.ts) */
.gm-pm-block { flex: none; align-self: center; max-width: 100%; display: flex; flex-direction: column; } /* as wide as its grid, in the middle of its column: the heading starts where the grid does */
.gm-pm-block > .gm-pm-lbl:first-child { height: var(--gm-h-sm); flex-wrap: nowrap; align-items: center; margin-bottom: var(--gm-sp-row); }
.gm-pm-col + .gm-pm-col { border-left: 1px solid var(--gm-l-line); padding-right: calc(env(safe-area-inset-right, 0px) + var(--gm-sp-section)); }
.gm-pm-chips { flex: none; min-width: 0; } /* a tab row (.gm-tabrow): 40 high, its chips in the middle, one row sliding sideways (below) */
.gm-pm-col > .gm-pm-lbl:first-child { height: var(--gm-h-sm); flex-wrap: nowrap; align-items: center; margin-bottom: var(--gm-sp-row); }
/* the NPC's grid and the bag's start on one line (用户 2026-10-13：「格子做到平齐吧」): the label over a grid is as high as the
   tab row (.gm-tabrow, 40) over the grid beside it */
.gm-pm-npc :is(.gm-pm-block, .gm-pm-col) > .gm-pm-lbl:first-child { height: var(--gm-h-touch); margin-bottom: 0; }
/* there both reach half the band (9) up into the column's padding (14): the chips stay where they were, the grids go 1 lower */
.gm-pm-npc :is(.gm-pm-block, .gm-pm-col) > :is(.gm-tabrow, .gm-pm-lbl:first-child) { margin-top: calc((var(--gm-h-sm) - var(--gm-h-touch)) / 2); }
.gm-pm-goldlbl { margin: var(--gm-sp-row) 0; align-items: center; } /* 存入 and the box under it 8 apart (点得到) */
.gm-pm-goldrow { display: flex; gap: var(--gm-sp-row); align-items: center; }
.gm-pm-goldin { flex: 1; min-width: 0; height: var(--gm-h-touch); font-variant-numeric: tabular-nums; }
/* 仓库's gold: 「仓库里 n 金币」 with 存入 (all of it) on its right; under it the box with 取出 on its right, as wide as its
   word; when it does not fit beside the box it goes to the next line, on the right, never stretched; a mistake under them all */
.gm-pm-goldbox { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: var(--gm-sp-row); }
.gm-pm-goldbox > .gm-pm-goldin { flex: 1 1 112px; }
.gm-pm-goldbox > .gm-pm-goldbtns { flex: none; }
.gm-pm-goldbox > .gm-say { flex: 0 0 100%; }
.gm-pm-claims { display: flex; flex-direction: column; gap: var(--gm-sp-hair); overflow: auto; min-height: 0; }
.gm-pm-claim { display: flex; align-items: center; gap: var(--gm-sp-row); padding: var(--gm-sp-hair) var(--gm-sp-near); border-radius: var(--gm-r-s); }
.gm-pm-claim span { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.gm-pm-claim small { font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-pm-claim > * { pointer-events: none; }
.gm-pm-none { padding: var(--gm-sp-row) 0; font-size: var(--gm-fs-label); color: var(--gm-c-dim); } /* a note under a list (「还有 n 件没看」): 注 12 */
.gm-pm-blank { padding: var(--gm-sp-row) 0; font-size: var(--gm-fs-body); color: var(--gm-c-dim); } /* an empty list's words (docs/ui-rules.md「页面的四种状态」): 正文 13 */
/* the picked thing under the bag: a section under a line (no frame in the frame), the HUD's card in it */
.gm-pm-pick { flex: 1; min-height: 0; display: flex; flex-direction: column; margin-top: var(--gm-sp-section); padding-top: var(--gm-sp-inner); border-top: 1px solid var(--gm-l-line); }
.gm-pm-pickwords { flex: 1; min-height: 0; overflow-y: auto; }
/* The menu's pages on a phone (panel-pages.ts split()): no title (the tab says it), two columns each scrolling on its own. */
.gm-page.phone .gm-page-head { margin-bottom: var(--gm-sp-row); }
.gm-page.phone .gm-ph { display: none; } /* its title and state are on the menu's head (phone-menu.ts) */
.gm-page.phone .gm-page-head:not(:has(> :not(.gm-ph))) { display: none; }
.gm-pm-npchead .gm-pm-headstate { flex: 1; text-align: right; font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-pm-npchead .gm-pm-headstate.gm-ph-parts { min-width: 0; display: flex; flex-wrap: wrap; justify-content: flex-end; white-space: normal; overflow: visible; } /* 你有 … 降龍币 · … 金币: the gold under the coins when one line is too narrow */
.gm-pm-npchead .gm-pm-headstate.gm-pm-headmoney { flex: 0 1 auto; margin-left: auto; } /* an NPC's sheet: the money on the name's line, at its right */
.gm-pm-npchead:has(> .gm-pm-headsay) { flex-wrap: wrap; row-gap: 0; padding-bottom: var(--gm-sp-near); }
.gm-pm-npchead > .gm-pm-headsay { flex: 1 0 100%; } /* what the NPC says: a line of its own under the name and the money */
.gm-pm-npchead:has(> .gm-say) { flex-wrap: wrap; row-gap: 0; padding-bottom: var(--gm-sp-near); }
.gm-pm-npchead > .gm-say { flex: 1 0 100%; } /* 铁匠: what the last thing done came to, the sheet's top line (提示的去处③) */
.gm-pm-headstate b { font-size: var(--gm-fs-body); font-weight: 500; color: var(--gm-c-main); font-variant-numeric: tabular-nums; }
.gm-pm-headstate b.gm-gold { color: var(--gm-c-gold); }
.gm-pm-headstate b.gm-coin { color: var(--gm-c-coin); }
.gm-page.phone .gm-pcol .gm-row.gm-nowrap { flex-wrap: wrap; } /* a box with buttons beside it: the box on its own line, the rest under it */
.gm-page.phone .gm-pcol .gm-row.gm-nowrap > .gm-input { flex: 1 0 100%; }
/* 宣战 on a phone: the price leaves the button (English 「Declare war 30,000 gold」 is wider than the column) and sits
   beside it in grey, under it when there is no room; the button itself never breaks */
.gm-war-cost { display: none; font-size: var(--gm-fs-label); color: var(--gm-c-sub); white-space: nowrap; }
.gm-page.phone .gm-pcol .gm-war-go small { display: none; }
.gm-page.phone .gm-pcol .gm-war-cost { display: inline; }
.gm-page.phone .gm-page-body:has(> .gm-pcols) { overflow: hidden; padding: 0; margin-right: 0; container: gm-pcols / inline-size; }
.gm-pcols { flex: 1; min-height: 0; display: flex; gap: var(--gm-sp-section); }
.gm-pcol { flex: 1 1 0; min-width: 0; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; gap: var(--gm-sp-section); padding-bottom: var(--gm-sp-inner); overscroll-behavior: contain; scrollbar-width: none; }
.gm-pcol + .gm-pcol { border-left: 1px solid var(--gm-l-line); padding-left: var(--gm-sp-section); }
.gm-pcol > :first-child { margin-top: 0; }
/* a group title not first in its column: 段 14 over it (the column's gap), 近 6 to its own things, as on a computer (.gm-grp's
   +4 / −4 is for the computer's body gap of 10) */
.gm-page.phone .gm-pcol > .gm-grp { margin: 0 0 calc(var(--gm-sp-near) - var(--gm-sp-section)); }
/* A text page's two columns are even, none pinned; each narrower than 200 (src/shared/space.ts PHONE_COL_MIN), they are one
   column scrolling top to bottom, the left's things first (docs/ui-rules.md「面板统一版式」, 用户 2026-10-15 批准草图
   https://claude.ai/artifact/RLXFZwuFm3LEPwpiJmwgVv ). 429 = 2 × 200 + the gap (段) + the right column's 段 and line. A grid
   page (交易行: the bag on the right, panel-pages.ts split's `grid`) shares its columns out itself and stays two. */
@container gm-pcols (width < 429px) {
  .gm-pcols:not(.gm-pcols-grid) { flex-direction: column; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; scrollbar-width: none; }
  .gm-pcols:not(.gm-pcols-grid) > .gm-pcol { flex: none; overflow: visible; }
  .gm-pcols:not(.gm-pcols-grid) > .gm-pcol:first-child { padding-bottom: 0; }
  .gm-pcols:not(.gm-pcols-grid) > .gm-pcol + .gm-pcol { border-left: 0; padding-left: 0; }
}
/* 行会's members in the narrow right column: a row too long for one line puts the leader's buttons on a second line, on the
   right (and a war's time left, 「Blackthorne1 Declared by us」 having no room for 「23 h 59 min left」); a row that fits stays one line (版面 第 4 步, 用户 2026-10-14 批准草图 https://claude.ai/artifact/6kj2Q6fzYL57FrVJ3F9JHh ). */
.gm-page.phone :is(.gm-members, .gm-apps, .gm-guilds, .gm-wars) tr { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gm-sp-row); padding: var(--gm-sp-tight) 0; border-bottom: 1px solid var(--gm-l-faint); }
.gm-page.phone :is(.gm-members, .gm-apps, .gm-guilds, .gm-wars) td { padding: 0; border: 0; white-space: nowrap; }
.gm-page.phone :is(.gm-members, .gm-apps, .gm-guilds, .gm-wars) td:first-child { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.gm-page.phone :is(.gm-members, .gm-apps, .gm-guilds, .gm-wars) td.r { margin-left: auto; display: flex; gap: var(--gm-sp-row); } /* the leader's two buttons 8 apart (点得到), not a space's width */
.gm-page.phone :is(.gm-apps, .gm-guilds) td:first-child { white-space: normal; } /* 申请人: the name over 职业 等级, the two buttons beside or under them; a guild to join: its name over its leader, 申请 beside or under */
.gm-page.phone .gm-members td.r:empty { display: none; }
.gm-page.phone .gm-members .gm-name-link { display: inline-block; vertical-align: middle; height: var(--gm-h-touch); line-height: var(--gm-h-touch); } /* a member's name a thumb high, the rows' padding 4 + 4 between two (点得到) */
.gm-page.phone .gm-lk-place { flex-direction: row; justify-content: space-between; align-items: center; }
/* one line each: a long English label (Blessed Armor) gives way first, the look's name keeps the room */
.gm-page.phone .gm-lk-place { gap: var(--gm-sp-near); }
.gm-page.phone .gm-lk-place > span { flex: 0 100 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.gm-page.phone .gm-lk-place > b { flex: 0 1 auto; min-width: 0; }
/* 商城 on a phone: its jump row a column on the left, the list on the right (样机) */
.gm-page.phone[data-tab="shop"]:has(.gm-page-head > .gm-jump) { flex-direction: row; }
.gm-page.phone[data-tab="shop"] .gm-page-head:has(> .gm-jump) { flex: 0 0 88px; margin: 0 var(--gm-sp-section) 0 0; display: flex; flex-direction: column; }
.gm-page.phone[data-tab="shop"] .gm-jump { flex: 0 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: none; flex-direction: column; margin-top: 0; gap: var(--gm-sp-tight); } /* the first one's top level with 「通行证」's 40 band beside it, the rest 4 apart (用户 2026-10-16); a screen too short for all seven (667 × 360: 5 short under the 46 head and the 14) scrolls the column, the keys keep 40 */
/* each a pill a thumb high, the column's width, its words 13 in the middle; 40 itself, so no band (用户 2026-10-16：又细又扁) */
.gm-page.phone[data-tab="shop"] .gm-jump .gm-chip { height: var(--gm-h-touch); line-height: var(--gm-h-touch); border-radius: var(--gm-r-full); font-size: var(--gm-fs-body); text-align: center; }
.gm-page.phone[data-tab="shop"] .gm-jump .gm-chip::after { content: none; }
/* 电脑上看手机版 (/play/?phone, src/render/resolution.ts phoneView): the page is an iPhone's screen held sideways in the
   window's top left corner; its transform makes it the box every fixed thing (canvases, pages, the menu) sits in. At
   (0, 0), so the pages placed by the window's coordinates (a canvas's getBoundingClientRect) land where they should: in
   the middle they were off by the frame's offset (用户 2026-10-13: the 充值 page's tiers at the bottom right). */
html.gm-phone-view body { position: relative; width: var(--gm-pv-w, 852px); height: var(--gm-pv-h, 393px); overflow: hidden; transform: translateZ(0); outline: 1px solid var(--gm-l-edge); } /* the phone screen's edge on the computer: a line, not a shadow */
/* iPhone from the home screen (src/render/resolution.ts fillIphoneScreen): the page is always the screen's size, never
   the viewport's, and its transform makes it the box every fixed thing sits in, as in the phone view. */
html.gm-fill, html.gm-fill body { width: var(--gm-screen-w); height: var(--gm-screen-h); }
html.gm-fill body { position: relative; overflow: hidden; transform: translateZ(0); }
/* 角色页 / 交易行: the picked thing's numbers under the bag, plainly (no card); not a box of its own that scrolls: the
   whole column scrolls, bag, numbers and buttons together, as the 仓库's (用户 2026-10-16: a box 3 lines high bounced
   back a long way) */
.gm-pm-detail { flex: none; display: flex; flex-direction: column; gap: var(--gm-sp-near); margin-top: var(--gm-sp-section); }
.gm-pm-detail:empty { display: none; }
/* its name in 楷书 (题字 17, as the phone's dialog titles; 字体规划), 「品质 · 部位」 on its right (item-facts.ts kindLine) */
.gm-pm-dhead { display: flex; align-items: baseline; justify-content: space-between; gap: var(--gm-sp-row); }
.gm-pm-dname { min-width: 0; font: 700 var(--gm-fs-phone) var(--gm-kai); }
.gm-pm-dkind { flex: none; font-size: var(--gm-fs-foot); color: var(--gm-c-dim); white-space: nowrap; }
.gm-pm-drows { display: flex; flex-wrap: wrap; gap: var(--gm-sp-tight) var(--gm-sp-page); } /* each value right after its name (用户 2026-10-13：「太开了」) */
.gm-pm-drows div { display: flex; align-items: baseline; gap: var(--gm-sp-near); }
/* 属性名 12 字·次, its value 13 字·主 a little heavier (500), as the tooltip's lines (docs/colors.md「面板和浮窗」) */
.gm-pm-drows span { font-size: var(--gm-fs-label); color: var(--gm-c-sub); }
.gm-pm-drows b { font-size: var(--gm-fs-body); font-weight: 500; font-variant-numeric: tabular-nums; }
/* a whole line as the tooltip's toned one (次数 3 · 每 30 秒回充 1 次): words and numbers inline, one size */
.gm-pm-drows .gm-pm-drun { display: block; }
.gm-pm-drows .gm-pm-drun span { font-size: var(--gm-fs-body); }
/* the better run, the worse, the same: 10 apart (6 + 4), the lines in a run 4, so the runs read as runs (用户 2026-10-16) */
.gm-pm-drows + .gm-pm-drows { margin-top: var(--gm-sp-tight); }
.gm-pm-drun .gm-pm-mark { margin-left: var(--gm-sp-near); }
/* a compare mark right after its value (item-detail.ts, docs/ui-rules.md「符号」): 注脚 bold, 好 / 坏; the better lines a run
   (.up), the worse a run under them (.down), the same last (.same) */
.gm-pm-mark { font-size: var(--gm-fs-foot); font-weight: 700; white-space: nowrap; }
.gm-pm-mark.good { color: var(--gm-c-good); }
.gm-pm-mark.bad { color: var(--gm-c-bad); }
.gm-pm-dfoot { margin: 0; font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-pm-dwhy { font-size: var(--gm-fs-foot); margin-top: calc(-1 * var(--gm-sp-tight)); } /* why its affix does not count, 注脚 right under the grey affix (item-facts.ts affixOff) */
/* the 需求行 as the tooltip's (need-line.ts needItems): each item a span, the short one .gm-bad, a gap apart (no 「·」,
   docs/ui-rules.md「符号」); one that does not fit starts a line */
.gm-pm-dneeds { display: flex; flex-wrap: wrap; gap: var(--gm-sp-hair) var(--gm-sp-section); }
/* 交易行 on a phone (panels.ts phoneMarket; 用户 2026-10-15 批准草图 https://claude.ai/artifact/DiaP2BmfqphKMHC96ETduz 「方案」): the
   left column only picks — 买东西 / 货架 as two even halves, 买东西's filter folded under one chip, the lots one a row (the
   picture one of the phone's cells, the name on one line, the price, the time left), a thumb high at least; picked, 线·金
   and 面·金 (面、线与按钮). The 卖金币 / 降龍币 row dashed, like the computer's empty slot. */
.gm-page .gm-pm-grid .gm-cell-it.sel { outline: var(--gm-sel-w) solid var(--gm-l-gold); outline-offset: calc(-1 * var(--gm-sel-w)); }
.gm-mk-filter { align-self: flex-start; max-width: 100%; display: flex; gap: var(--gm-sp-tight); }
.gm-mk-fl { min-width: 0; overflow: hidden; text-overflow: ellipsis; } /* the narrowest phone, a currency and 价格 ↑ picked: the words give way, ▾ stays */
.gm-mk-opts { display: flex; flex-direction: column; gap: var(--gm-sp-page); margin-top: var(--gm-sp-tight); } /* 18 under the chip over it, as between its own rows */
.gm-mk-opts .gm-chips { row-gap: var(--gm-sp-page); } /* chips 22 high 18 apart: each one's 40 band never in the next row's (点得到) */
.gm-mk-terms { margin: 0; font-size: var(--gm-fs-label); }
.gm-mk-rows { display: flex; flex-direction: column; gap: var(--gm-sp-near); }
.gm-mk-row { box-sizing: border-box; min-height: var(--gm-h-touch); display: flex; align-items: center; gap: var(--gm-sp-near); padding: var(--gm-sp-near); border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m); background: var(--gm-s-card); cursor: pointer; transition: background var(--gm-t-quick) var(--gm-ease-out); }
.gm-mk-row.on { border-color: var(--gm-l-gold); background: var(--gm-s-gold); }
.gm-mk-row > .gm-pic { width: var(--gm-cell); height: var(--gm-cell); } /* one of the phone's cells (phone-cell.ts phoneCell) */
.gm-mk-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: var(--gm-fs-label); line-height: 1.3; }
.gm-mk-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-mk-row.amt { flex-wrap: wrap; row-gap: var(--gm-sp-tight); } /* an amount's row (panels.ts lotRow): its words never narrower than the amount (min-width: auto), so too wide beside the picture they go under it, the whole width */
.gm-mk-row.amt > .gm-mk-txt { min-width: auto; }
.gm-mk-txt .pr { font-variant-numeric: tabular-nums; }
.gm-mk-st { font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-mk-st.gm-gold { color: var(--gm-c-gold); }
.gm-mk-row.add { justify-content: center; text-align: center; border-style: dashed; background: none; color: var(--gm-c-sub); font-size: var(--gm-fs-label); }
.gm-mk-row.add.on { color: var(--gm-c-gold); background: var(--gm-s-gold); }
@media (hover: hover) { .gm-mk-row:hover { background: var(--gm-s-raise); } .gm-mk-row.on:hover { background: var(--gm-s-gold-hover); } }
.gm-mk-dprice { font-size: var(--gm-fs-body); }
.gm-page.phone .gm-pm-detail .gm-unfold .gm-seg { display: grid; grid-template-columns: 1fr 1fr; width: 100%; font-size: var(--gm-fs-label); } /* 卖降龍币 / 卖金币 as two even halves of the column */
.gm-page.phone .gm-pm-detail .gm-unfold .gm-seg button { padding: 0 var(--gm-sp-tight); }
.gm-pm-dtext { margin: 0; font-size: var(--gm-fs-body); line-height: 1.6; color: var(--gm-c-sub); }
/* 交易行 on a phone: the 挂出 box under the picked thing's numbers, no frame of its own and no second name */
.gm-page.phone .gm-pm-detail .gm-unfold { padding: 0; border: 0; background: none; }
.gm-page.phone .gm-pm-detail .gm-unfold .gm-unf-name { display: none; }
.gm-page.phone .gm-pm-detail .gm-unfold .gm-row { flex-wrap: wrap; } /* the right column is narrow: a box, its unit and the buttons go down a line when they must */
.gm-pm-dcore { margin: 0; font-size: var(--gm-fs-body); color: var(--gm-c-sub); }
.gm-pm-dcore b { font-size: var(--gm-fs-body); font-weight: 500; color: var(--gm-c-main); user-select: text; } /* as a value: 13 字·主 稍粗, no bigger than the stats (用户 2026-10-16：「字体太大了」); not gold, gold is money in a window */
/* 挂出 / 取消 at the start of their row like every phone button row, the fee after them, one height */
.gm-page.phone .gm-pm-detail .gm-unfold .gm-row:last-child > .gm-grow { display: none; }
.gm-page.phone .gm-pm-detail .gm-unfold .gm-row:last-child > .gm-muted { order: 3; margin-left: var(--gm-sp-row); }
.gm-page.phone .gm-pm-detail .gm-btn.gold { height: var(--gm-h-touch); line-height: var(--gm-h-touch); padding: 0 var(--gm-sp-section); }
/* the phone menu's button rows: the gold one as tall as the rest (用户 2026-10-13：「这个穿上的按钮 是不是不太对劲」) */
.gm-pm .gm-pm-acts .gm-btn.gold { height: var(--gm-h-touch); line-height: var(--gm-h-touch); padding: 0 var(--gm-sp-section); }

/* ---- A phone's 弹窗 (docs/ui-rules.md「弹窗」, 用户 2026-10-13, 草图 https://claude.ai/artifact/V6Xis2vhuuKWCqka3U1YyM) ------------- */
/* ① a question in the middle (393 high: at 38% it met the map's name), no keys on its buttons (ui.ts dialogButton). Thinner
   on a phone (用户 2026-10-14 选 C, 草图 https://claude.ai/artifact/EEbksYZDypWhDPHSyzncf3 ): 280 wide, the head no bar (楷书 17
   as the first line, no rule under it), the buttons 40 like every phone button (「做」 told by its gold, not its size); with one button and no grey note it sits right of the text (出错了's note can be long:
   its button stays under it). Long strings in the text wrap (an error's URL). The pass and bind offers (.gm-pass,
   .gm-sheet) keep their own. */
html.gm-touch-on .gm-dlg { top: 50%; }
/* typing in one of its boxes, it sits on the keyboard (play-page.ts: gm-kb-on, --gm-kb-bottom = the seen part's bottom in the
   box it is placed in): too tall for what is left, its head goes, not its box */
html.gm-touch-on.gm-kb-on .gm-dlg:not(.gm-pass) { top: calc(var(--gm-kb-bottom) - var(--gm-sp-row)); transform: translate(-50%, -100%); }
html.gm-touch-on.gm-kb-on .gm-dlg .gm-say:empty { min-height: 0; } /* no empty line kept for a mistake while the room is short */
/* one box (改名): typing, it becomes a strip, the title, the box and the buttons on one line, the grey note and a mistake
   under them, the line above the box left out until the keyboard goes (用户 2026-10-14, 草图 https://claude.ai/artifact/3D1BwJS5fzUz3ygs2MuAhr ) */
html.gm-touch-on.gm-kb-on .gm-dlg.gm-ask:has(.gm-ask-input) { width: min(560px, calc(100% - 2 * var(--gm-sp-page))); display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--gm-sp-inner); align-items: center; padding: var(--gm-sp-inner) var(--gm-sp-section); }
html.gm-touch-on.gm-kb-on .gm-dlg.gm-ask:has(.gm-ask-input) > .gm-ph { grid-row: 1; grid-column: 1; padding: 0; margin: 0; }
html.gm-touch-on.gm-kb-on .gm-dlg.gm-ask:has(.gm-ask-input) > p { display: none; }
html.gm-touch-on.gm-kb-on .gm-dlg.gm-ask:has(.gm-ask-input) > .gm-ask-input { grid-row: 1; grid-column: 2; margin: 0; }
html.gm-touch-on.gm-kb-on .gm-dlg.gm-ask:has(.gm-ask-input) > .gm-row { grid-row: 1; grid-column: 3; margin: 0; flex-wrap: nowrap; }
html.gm-touch-on.gm-kb-on .gm-dlg.gm-ask:has(.gm-ask-input) > .gm-say,
html.gm-touch-on.gm-kb-on .gm-dlg.gm-ask:has(.gm-ask-input) > .gm-ask-note { grid-column: 1 / -1; margin-top: var(--gm-sp-near); }
html.gm-touch-on .gm-dlg:not(.gm-pass) { width: 280px; padding: 0 var(--gm-sp-section) var(--gm-sp-section); }
html.gm-touch-on .gm-dlg:not(.gm-pass) > .gm-ph { height: auto; padding-top: var(--gm-sp-section); margin-bottom: var(--gm-sp-near); border-bottom: 0; align-items: baseline; }
html.gm-touch-on .gm-dlg:not(.gm-pass) > .gm-ph > b { font-size: var(--gm-fs-phone); }
html.gm-touch-on .gm-dlg:not(.gm-pass) .gm-row { margin-top: var(--gm-sp-inner); }
html.gm-touch-on .gm-dlg:not(.gm-pass) p em, html.gm-touch-on .gm-dlg.gm-alarm .gm-ask-note { overflow-wrap: anywhere; } /* and 出错了's detail: the browser's own words, a URL in them */ /* a player's name in a question (em()) is what a player typed: it may break anywhere; our own words only between words (「换行」) */
html.gm-touch-on .gm-dlg input.gm-input { height: var(--gm-h-touch); } /* a box can have no unseen band: it is 40 itself (点得到) */
html.gm-touch-on .gm-dlg:not(.gm-pass):has(> .gm-row > .gm-btn:only-child):not(:has(.gm-ask-note)) { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--gm-sp-section); align-items: end; }
html.gm-touch-on .gm-dlg:not(.gm-pass):has(> .gm-row > .gm-btn:only-child):not(:has(.gm-ask-note)) > .gm-ph { grid-column: 1 / -1; }
html.gm-touch-on .gm-dlg:not(.gm-pass):has(> .gm-row > .gm-btn:only-child):not(:has(.gm-ask-note)) .gm-row { margin-top: 0; }
/* a phone's own number pad (numpad.ts; docs/ui-rules.md「输入框和键盘」, 用户 2026-10-14 选 A): beside a number box, in front of
   the bag; keys a thumb's 40 high, 8 apart; the digits 跳字 17; no button to close it, a tap elsewhere does */
.gm-numpad { position: fixed; z-index: var(--gm-z-pop); display: grid; grid-template-columns: repeat(3, 52px); gap: var(--gm-sp-row); padding: var(--gm-sp-row);
  background: var(--gm-s-float); border: 1px solid var(--gm-l-edge); border-radius: var(--gm-r-m); } /* no shadow on a phone: its edge only, as the sheets */
.gm-np-k { all: unset; box-sizing: border-box; height: var(--gm-h-touch); border-radius: var(--gm-r-full); background: var(--gm-s-raise); display: grid; place-items: center;
  font: 700 var(--gm-fs-phone) var(--gm-num); color: var(--gm-c-main); font-variant-numeric: tabular-nums; transition: background var(--gm-t-quick) var(--gm-ease-out); }
html.gm-press .gm-np-k:active { background: var(--gm-s-hover); } /* 按下 = 指着: a key 凸 → 悬停, as every button */
.gm-np-k.q { font: 500 var(--gm-fs-emph) var(--gm-hei); color: var(--gm-c-sub); }
.gm-input.gm-np-on { border-color: var(--gm-l-gold); } /* the box the pad types into */
/* a phone's typing strip (type-strip.ts; docs/ui-rules.md「输入框和键盘」): over the keyboard, what is typed in 楷书 17, the
   box, the box's Enter as a button, its note or mistake under them */
.gm-tstrip { position: fixed; z-index: var(--gm-z-ask); left: calc(env(safe-area-inset-left, 0px) + var(--gm-sp-page)); right: calc(env(safe-area-inset-right, 0px) + var(--gm-sp-page));
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--gm-sp-inner); align-items: center; padding: var(--gm-sp-inner) var(--gm-sp-section);
  background: var(--gm-s-float); border: 1px solid var(--gm-l-gold); border-radius: var(--gm-r-m); font: var(--gm-fs-body) var(--gm-hei); color: var(--gm-c-main); } /* no shadow on a phone: its edge only */
.gm-tstrip > b { font: 700 var(--gm-fs-phone) var(--gm-kai); color: var(--gm-c-gold); white-space: nowrap; }
.gm-tstrip > .gm-input { box-sizing: border-box; width: 100%; height: var(--gm-h-touch); border-color: var(--gm-l-gold); }
.gm-tstrip > textarea.gm-input { height: auto; padding: var(--gm-sp-row) var(--gm-sp-inner); line-height: 1.6; resize: none; }
.gm-ts-note { grid-column: 2 / -1; margin: var(--gm-sp-near) 0 0; font-size: var(--gm-fs-label); color: var(--gm-c-dim); }
.gm-ts-note.bad { color: var(--gm-c-bad); }
/* ② the notice slot: one card at a time in the top middle, the rest wait (ui.ts notice, placeNotices sets its top) */
#gm-slot { position: fixed; z-index: var(--gm-z-pop); left: 50%; top: 56px; transform: translateX(-50%); }
#gm-slot .gm-inv { box-shadow: none; width: auto; max-width: calc(100vw - 32px); min-height: 52px; gap: var(--gm-sp-row); padding: var(--gm-sp-near) var(--gm-sp-near) var(--gm-sp-near) var(--gm-sp-section); font-size: var(--gm-fs-body); }
#gm-slot .gm-inv button { width: var(--gm-h-touch); height: var(--gm-h-touch); line-height: var(--gm-h-touch); font-size: var(--gm-fs-emph); }
#gm-slot .gm-inv .gm-btn { width: auto; padding: 0 var(--gm-sp-section); }
.gm-inv-more { margin-left: var(--gm-sp-row); font-size: var(--gm-fs-label); color: var(--gm-c-dim); white-space: nowrap; }
/* the pass and bind offers' card, opened from the slot: a sheet from the right like the menu's, the fight's strip left of
   it (a tap there closes it; docs/ui-rules.md「面板统一版式」) */
html.gm-touch-on .gm-dlg.gm-sheet { left: calc(env(safe-area-inset-left, 0px) + var(--gm-strip)); right: 0; top: 0; bottom: 0; width: auto; transform: none; overflow-y: auto;
  background: var(--gm-s-sheet); border: 0; border-left: 1px solid var(--gm-l-edge); border-radius: 0; padding-right: calc(env(safe-area-inset-right, 0px) + var(--gm-sp-page)); }
.gm-sheet-scrim { position: fixed; left: 0; top: 0; bottom: 0; width: calc(env(safe-area-inset-left, 0px) + var(--gm-strip)); z-index: var(--gm-z-pop); background: var(--gm-scrim); }
html.gm-sheet-on .gm-touch > :not(.gm-t-safe) { display: none; }
/* ④ the player menu (a long press on a player, a party key): a thumb's 40 a line */
html.gm-touch-on .gm-item { min-height: var(--gm-h-touch); }
/* 点得到 (docs/ui-rules.md, 规范检查 2026-10-13): in the phone's sheets every button and box is a thumb high (40),
   buttons in a row 8 apart; chips keep their look and reach 40 with a band over and under them nobody sees.
   Every button on a phone is 40 to see, not only to tap — the 问答框's and the typing strip's too: one size, 「做」 told by
   its colour (gold), not its size — and as wide as its words, 14 each side, never stretched to share a row (主会话
   2026-10-16; the login and character cards, #lobby .gm-lob-card, keep their own). :is() for the weight of .gm-btn.gold.md. */
html.gm-touch-on :is(.gm-btn, .gm-btn.gold.md) { height: var(--gm-h-touch); line-height: var(--gm-h-touch); padding: 0 var(--gm-sp-section); }
.gm-pm .gm-btn, .gm-page.phone .gm-btn, .gm-page.phone input.gm-input, .gm-page.phone .gm-seg { height: var(--gm-h-touch); line-height: var(--gm-h-touch); }
.gm-page.phone .gm-seg button { line-height: normal; }
.gm-page.phone .gm-set-row { height: var(--gm-h-touch); }
.gm-page.phone .gm-set-solo { height: var(--gm-h-touch); }
.gm-page.phone .gm-who { height: var(--gm-h-touch); }
.gm-pm .gm-range, .gm-page.phone .gm-range, html.gm-touch-on .gm-dlg .gm-range { height: var(--gm-h-touch); --gm-range-thumb: 24px; } /* 滑动条: a thumb's 40 to grab, the thumb 24 */
html.gm-press .gm-range:active::-webkit-slider-thumb, html.gm-press .gm-range::-webkit-slider-thumb:active { background: var(--gm-s-raise); }
html.gm-press .gm-range:active::-moz-range-thumb, html.gm-press .gm-range::-moz-range-thumb:active { background: var(--gm-s-raise); } /* 送人's list of people: a row a thumb high (规范检查 2026-10-15) */
.gm-pm .gm-chip, .gm-page.phone .gm-chip { position: relative; }
/* 40 across too (点得到, 主会话 2026-10-16): a short chip (仓库's page 「1」) is 40 wide itself, so its band is never cut by its
   column's edge nor reaches a neighbour 6 away */
.gm-pm .gm-chip, .gm-page.phone .gm-chip { min-width: var(--gm-h-touch); }
.gm-pm .gm-chip::after, .gm-page.phone .gm-chip::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: var(--gm-h-touch); transform: translateY(-50%); }
/* A row of small tabs at a column's top or right over a grid (仓库's pages, 铁匠's 今天的货 / 买回, 交易行's 买东西 / 我的货架,
   告示栏's 赞最多 / 最新 / 我的) takes up 40 itself, its chips (22) in the middle, so their bands are inside the row: never
   cut off by the column's top, never under the grid's cells (用户 2026-10-15 批准草图 https://claude.ai/artifact/2ZCpPbkRJ7ujDZK8tapTuu ).
   In a column (gap 14) the gap under the chips stays 14: the band's half under them (9) is in it. */
:is(.gm-pm, .gm-page.phone) .gm-tabrow { box-sizing: border-box; margin-bottom: 0; padding-block: calc((var(--gm-h-touch) - var(--gm-h-sm)) / 2); align-items: center; flex-wrap: wrap; }
.gm-page.phone .gm-pcol > .gm-tabrow { margin-bottom: calc((var(--gm-h-sm) - var(--gm-h-touch)) / 2); }
/* An NPC sheet's tab row over its grid (仓库's pages and 待领取, 铁匠's 买东西 / 买回; phone-menu.ts chips) never folds, a fold
   would push the grid below the bag's beside it: one row sliding sideways in its own 40 band, no scroll bar, the chip that
   does not fit cut by the column's edge (用户 2026-10-17 选 A, 草图 https://claude.ai/artifact/NrxvbyactM3YdGmpSTGpfx ⑧).
   A side with more to slide to fades out over 段 14 into the sheet's own ground (.cut-l / .cut-r, phone-menu.ts slideTabs):
   a mask, no colour of its own — the opaque 面·页 only says "show". */
.gm-pm .gm-pm-chips.gm-tabrow { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scrollbar-width: none; }
.gm-pm .gm-pm-chips.gm-tabrow::-webkit-scrollbar { display: none; }
.gm-pm .gm-pm-chips.gm-tabrow > .gm-chip { flex: none; }
.gm-pm .gm-pm-chips.cut-r { mask-image: linear-gradient(to right, var(--gm-s-sheet) calc(100% - var(--gm-sp-section)), transparent); }
.gm-pm .gm-pm-chips.cut-l { mask-image: linear-gradient(to left, var(--gm-s-sheet) calc(100% - var(--gm-sp-section)), transparent); }
.gm-pm .gm-pm-chips.cut-l.cut-r { mask-image: linear-gradient(to right, transparent, var(--gm-s-sheet) var(--gm-sp-section), var(--gm-s-sheet) calc(100% - var(--gm-sp-section)), transparent); }
/* Two columns side by side start on one middle line (用户 2026-10-15 批准草图 https://claude.ai/artifact/BwmTBVeXrQg9kW441i4DJQ ⑦):
   on a phone every column's first row is a 40 band with its content in the middle — a tab row (.gm-tabrow), a group title
   (.gm-grp, 告示栏's 写意见, 商城's 通行证), the 背包 line (交易行), 交易行's 买东西 / 货架 (40 itself), 商城's first jump pill (40
   itself) —, so the middles meet whatever each row holds. A group title's band reaches 9 into the gap under it, as a tab row's. */
.gm-page.phone :is(.gm-pcol, .gm-page-body) > .gm-grp:first-child, .gm-page.phone .gm-pcol > :first-child > .gm-grp:first-child { box-sizing: border-box; min-height: var(--gm-h-touch); margin-top: 0; margin-bottom: calc((var(--gm-h-sm) - var(--gm-h-touch)) / 2); }
.gm-page.phone .gm-pcol > .gm-pm-block:first-child > .gm-pm-lbl:first-child { height: var(--gm-h-touch); margin-bottom: 0; }
.gm-page.phone .gm-tabrow .gm-r { white-space: normal; min-width: min(100%, 10em); } /* 交易行's 「卖出扣 … · 挂 …」 beside the tabs, under them when less than 10 characters' room is left: folded, never out of the column */
.gm-page.phone .gm-board-days { white-space: nowrap; } /* 告示栏's 「最近 7 天」: one line, under the tabs when the column is narrow */
/* Chips that wrap onto more rows (交易行's kinds, a tab row's grey words under its chips; an NPC sheet's tab row slides instead): the rows 40 − 22 = 18 apart, each band half the gap
   over and under, the next row's band just meeting it (用户 2026-10-15 选 A, the same sketch) */
:is(.gm-pm, .gm-page.phone) :is(.gm-chips, .gm-tabrow) { row-gap: calc(var(--gm-h-touch) - var(--gm-h-sm)); }
/* the names under a stage (商城, 外观·技能; .gm-name about 26 high) keep their look too: the rows go 14 apart (40 − 26) and each
   name's band reaches half the gap over and under it, 40 in all, never into the next name's (用户 2026-10-15：「按钮撑到 40」) */
.gm-page.phone .gm-names { row-gap: var(--gm-sp-section); }
.gm-page.phone .gm-names-job { margin-bottom: calc(var(--gm-sp-hair) - var(--gm-sp-section)); } /* a job's head stays 2 over its names (the first band may reach it: it is no button) */
.gm-page.phone .gm-name { position: relative; min-height: calc(var(--gm-h-touch) - var(--gm-sp-section)); }
.gm-page.phone .gm-name::after { content: ""; position: absolute; left: 0; right: 0; top: calc(var(--gm-sp-section) / -2); bottom: calc(var(--gm-sp-section) / -2); }
/* a phone's chat: a clicked item link's numbers, plainly, over the lines (play-page.ts syncLinkCard; 「字体规划」 no card shrunk) */
.gm-linkcard { position: fixed; z-index: var(--gm-z-ui); box-sizing: border-box; overflow-y: auto; display: flex; flex-direction: column; gap: var(--gm-sp-near); padding: var(--gm-sp-inner); background: var(--gm-s-float); border: 1px solid var(--gm-l-line); border-radius: var(--gm-r-m); font: var(--gm-fs-body) var(--gm-hei); color: var(--gm-c-main); pointer-events: auto; }
.gm-linkcard[hidden] { display: none; }

/* 选中的行 (docs/ui-rules.md「选择」, 用户 2026-10-17 照推荐): 面·金 and a 2 wide 线·金 bar down its left, 近 6 in from its top and
   bottom — a list's rows and the phone's tab columns alike (the chat sheet's, drawn on the canvas, the same: hud.ts SEL_W).
   Painted as the row's own background (one layer under the words), last in the sheet so a row's own rules never cover it. */
:is(.gm-name.on, .gm-lk-skill.on, #ui .gm-pm-tabs button.on, .gm-pm-claim.sel, .gm-t-sk.sel) { background: linear-gradient(var(--gm-l-gold), var(--gm-l-gold)) 0 50% / var(--gm-sel-w) calc(100% - 2 * var(--gm-sp-near)) no-repeat, var(--gm-s-gold); }
