/* ============================================================
   Pixel Life Online — interface system
   Type: pixel display face for labels/numbers, sans body face for prose.
   Layout: HUD strip / icon rail / full-bleed world / sliding dock / action bar
   ============================================================ */

/* The display face ships with the game rather than being fetched from a content delivery
   network: the interface is Chinese first, the Latin it pairs with is not decorative — it is
   every number the player reads — and a font that cannot be reached from the player's own
   network takes the whole pixel identity with it. Subset to Basic Latin, 4.7 KB, SIL OFL 1.1
   (web/fonts/OFL-press-start-2p.txt). */
@font-face {
  font-family: 'Press Start 2P';
  font-style: normal;
  /* one file, and it is offered for every weight the sheet asks for. Chrome makes each
     <b> bolder than its parent, so a plain <b>80</b> inside a 700 row arrives at 900 —
     and if no face claims that weight the browser draws one, thickening and smearing an
     8-pixel grid that was never meant to have a bold. Declaring the range says the truth:
     this face has no bold, and the emphasis is carried by size and colour instead. */
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/press-start-2p-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* The same file, offered as the italic the sheet never asks for and never should get.
   Measured: with only the upright face declared above, a request for italic 像素 digits
   comes back 27% heavier — the browser shears the glyph and repaints it, which cuts an
   8-pixel grid diagonally. Pointing the slant at the upright face makes the fake impossible. */
@font-face {
  font-family: 'Press Start 2P';
  font-style: italic;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/press-start-2p-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --bg-void: #05060f;
  --bg-dark: #0a0a1a;
  --bg-mid: #12142a;
  --bg-light: #1b1e38;
  --bg-raise: #242848;
  --line: #2e3357;
  --line-soft: #22263f;

  --accent: #4a9eff;
  --accent-2: #44ddff;
  --highlight: #ff4477;
  --gold: #ffd166;
  --gold-solid: #f5c542;
  --success: #4ade80;
  --danger: #ff6b6b;
  --purple: #b794f4;

  --text: #eaecff;
  --text-2: #aab0d8;
  --text-dim: #6f76a3;

  /* the pixel face falls back on the voice the interface already speaks, never on a terminal
     font: if a Chinese word ever reaches it, it should arrive as that word, not as a box */
  --font-display: 'Press Start 2P', var(--font-body);
  --font-body: system-ui, -apple-system, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;

  /* ---------- the type scale ----------
     Two voices, and neither one borrows the other's glyphs. The pixel face carries Latin and
     numerals only — it has no Chinese in it, so a CJK word set in it is silently drawn by a
     different family at a different weight on the same line, which is what made the interface
     look printed by accident. Chinese therefore rides the body face at a size chosen from this
     ladder, and the ladder has a floor: below 12px a 横 and a 竖 merge into one grey smudge. */
  --t-1: 12px;   /* meta, timestamps, chip labels — the floor */
  --t-2: 13px;   /* the running voice: log lines, options, form text */
  --t-3: 14px;   /* a sentence the player has to read once and act on */
  --t-4: 16px;   /* a panel's own heading */
  --t-5: 20px;   /* a screen's heading */
  --t-6: 26px;   /* the one big number on a card */
  /* the pixel face is drawn on an 8-pixel grid, so it is only ever set at a size that grid
     divides into. Anything else gets rescaled and the whole point of a pixel font — a hard
     edge — turns to grey. Four sizes, and each one is chosen for what it stands next to. */
  --d-1: 8px;    /* a code that stands alone in a tight column: D138, x3, a bar value */
  --d-2: 12px;   /* a number that shares a line with Chinese — 12px matches a 13px glyph box */
  --d-3: 16px;   /* the clock */
  --d-4: 24px;   /* the one big Latin token on a screen */
  /* a control is a fixed height first and its text second, so a label that changes language,
     grows a glyph or loses a disabled state cannot move the row around it */
  --ctl-sm: 32px;
  --ctl-md: 40px;
  --ctl-lg: 48px;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;
  --r: 3px;
  --r-2: 5px;
  --hud-h: 74px;
  --bar-h: 78px;
  --rail-w: 68px;
  --dock-w: 430px;

  --shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
  /* the recipe every pressable thing shares: a lit top edge, a shaded bottom edge, and a hard
     plinth it stands on that collapses when it is pushed. No blur on the plinth — a soft
     shadow under a pixel key is the thing that makes pixel art look out of focus. */
  --key: inset 0 1px 0 rgba(255, 255, 255, 0.26), inset 0 -2px 0 rgba(0, 0, 0, 0.34), 0 3px 0 rgba(0, 0, 0, 0.45), 0 8px 18px rgba(0, 0, 0, 0.34);
  --key-flat: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body { height: 100%; }

body {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.6;
  background: var(--bg-void);
  color: var(--text);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

button { font: inherit; color: inherit; }

/* ---------- sprite icons ----------
   Every glyph is a 16x16 pixel sprite baked into a background-image by icons.js.
   The base class sizes from font-size, so each surface below just picks a weight. */
.ico {
  display: inline-block;
  width: 1em;
  height: 1em;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  vertical-align: -0.15em;
  flex: 0 0 auto;
}
.rail-icon .ico { font-size: 20px; }
.dock-icon .ico { font-size: 18px; }
.hot-icon .ico { font-size: 22px; }
.action-btn .action-icon .ico { font-size: 20px; }
.action-btn .ico { vertical-align: -0.18em; }
.modal-header .ico { font-size: 1.1em; }
.hud-value .ico { font-size: 12px; }
.hot-cost .ico, .action-detail .ico, .news-item .ico, .list-item .ico, .pane-hint .ico { font-size: 1.05em; }
.log-entry .ico, .ticker-row .ico { font-size: 13px; }
.notification .ico { font-size: 16px; margin-right: 2px; }
.chip .ico, .pixel-btn .ico { font-size: 1.25em; }
.section-title .ico { font-size: 14px; }
/* sprites carry their own ink outline, so a soft drop shadow keeps them legible on light panels */
.ico { filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.45)); }

/* ---------- interaction feedback ---------- */
.pixel-btn:focus-visible, .rail-btn:focus-visible, .hot-btn:focus-visible,
.action-btn:focus-visible, .chip:focus-visible, .list-item:focus-visible, .sleep-btn:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
.rail-btn { transition: background 0.14s ease, color 0.14s ease, border-left-color 0.14s ease, transform 0.14s ease; }
.rail-btn:hover { transform: translateX(2px); }
.rail-btn:active { transform: translateX(2px) scale(0.97); }
.rail-btn.active .rail-icon .ico { filter: drop-shadow(0 0 6px rgba(255, 209, 102, 0.55)); }
.hot-btn { transition: transform 0.14s ease, border-color 0.14s ease, background 0.14s ease, box-shadow 0.14s ease; }
.hot-btn:hover:not(:disabled) { box-shadow: 0 6px 14px rgba(0, 0, 0, 0.45); }
.hot-btn:active:not(:disabled) { transform: translateY(0) scale(0.97); }
.action-btn { transition: transform 0.14s ease, border-left-color 0.14s ease, background 0.14s ease; }
.action-btn:active:not(:disabled) { transform: translateX(2px) scale(0.995); }
.list-item { transition: background 0.14s ease, transform 0.14s ease; }
.list-item:hover { transform: translateX(2px); }
.chip { transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease, transform 0.12s ease; }
.chip:active { transform: translateY(1px); }
.dock.in { animation: dockIn 0.2s cubic-bezier(0.2, 0.9, 0.25, 1); }
.dock.in .dock-body { animation: panelFade 0.26s ease-out both; }
@keyframes panelFade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.modal.active .modal-content { animation: modalPop 0.18s cubic-bezier(0.2, 0.9, 0.25, 1); }
@keyframes pulseGold { 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 209, 102, 0); } 50% { box-shadow: 0 0 0 3px rgba(255, 209, 102, 0.35); } }
.rail-btn.pulse { animation: pulseGold 1.1s ease-in-out 2; }

/* ---------- day transition card ---------- */
.day-card {
  position: absolute; inset: 0; z-index: 6;
  display: grid; place-items: center; pointer-events: none;
}
.day-card .dc-frame {
  min-width: 250px; padding: 20px 30px 18px; text-align: center;
  background: rgba(8, 10, 22, 0.88);
  border: 2px solid var(--gold);
  box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.55), 0 16px 44px rgba(0, 0, 0, 0.6);
  animation: cardIn 0.34s cubic-bezier(0.2, 0.9, 0.25, 1);
}
.day-card.out { animation: cardOut 0.55s ease-in forwards; }
.dc-kicker {
  font-size: var(--t-1);
  font-weight: 700;
  letter-spacing: 4px;
  color: var(--text-dim);
}
/* the widest type in the game is a Chinese phrase, and Chinese at 26px is 26 ideographs —
   a frame's worth of ink. It gets one pixel of shadow, not the three a Latin capital can
   afford, or the strokes fill in and the day reads as a gold bar. */
.dc-day { font-size: var(--t-6); font-weight: 800; letter-spacing: 2px; color: var(--gold); margin: 10px 0 8px; text-shadow: 0 2px 0 rgba(0, 0, 0, 0.7); }
.dc-meta { display: flex; align-items: center; justify-content: center; gap: 8px; font-size: var(--t-2); color: var(--text-2); }
.dc-meta .ico { font-size: 17px; }
.dc-dot { width: 3px; height: 3px; background: var(--text-dim); display: inline-block; }
.dc-sick {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  margin-top: 10px; padding-top: 8px; font-size: var(--t-1); color: #ffc9c9;
  border-top: 1px solid rgba(255, 107, 107, 0.3);
}
.dc-sick .ico { font-size: 14px; }
.dc-stage {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin-top: 9px; padding-top: 8px; font-size: var(--t-1); color: var(--text-2);
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.dc-stage .ico { font-size: 15px; }
.dc-stage b { font-family: var(--font-display); font-size: var(--d-2); color: var(--gold); }
.dc-stage.tone-great b, .dc-stage.tone-good b { color: var(--success); }
.dc-stage.tone-bad b { color: var(--danger); }
.dc-open { color: var(--text-dim); }
@keyframes cardIn { from { opacity: 0; transform: scale(0.82) translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes cardOut { to { opacity: 0; transform: translateY(-28px) scale(1.05); } }

/* ---------- sleep: the night plays out, fast-forwarded ---------- */
.sleep-overlay {
  position: absolute; inset: 0; z-index: 30;
  display: grid; place-items: center;
  background: radial-gradient(120% 92% at 50% 40%, rgba(15, 17, 44, 0.86), rgba(3, 4, 12, 0.97));
  animation: sleepIn 0.6s ease-out;
}
.so-inner { text-align: center; display: flex; flex-direction: column; align-items: center; gap: 11px; }
.so-moon { font-size: 44px; filter: drop-shadow(0 0 14px rgba(159, 182, 255, 0.55)); animation: soDrift 6s ease-in-out infinite alternate; }
.zzz { position: relative; height: 24px; width: 60px; font-family: var(--font-display); font-size: var(--d-3); color: #cdd6ff; }
.zzz span { position: absolute; left: 8px; opacity: 0; animation: zFloat 2.6s linear infinite; }
.zzz span:nth-child(2) { animation-delay: 0.9s; }
.zzz span:nth-child(3) { animation-delay: 1.8s; }
/* the plan line is a clock and a Chinese remainder in the same string, so it rides the body
   face with tabular figures: the numerals keep their width as the countdown ticks, and 还剩
   is not drawn by a fallback halfway across the line */
.so-plan { font-size: var(--t-3); font-weight: 600; color: var(--gold); letter-spacing: 0.5px; font-variant-numeric: tabular-nums; }
.so-bar { width: 236px; height: 10px; border: 2px solid #4b5490; background: #0a0c1c; }
.so-fill { height: 100%; width: 0; background: linear-gradient(90deg, #5a63b8, #cdd6ff); }
.so-note { font-size: var(--t-2); color: var(--text-dim); max-width: 330px; line-height: 1.6; }
@keyframes sleepIn { from { opacity: 0; } }
@keyframes soDrift { from { transform: translateX(-14px); } to { transform: translateX(14px); } }
@keyframes zFloat { 0% { opacity: 0; transform: translate(0, 0) scale(0.7); } 22% { opacity: 1; } 100% { opacity: 0; transform: translate(30px, -36px) scale(1.3); } }

/* ---------- morning report card ---------- */
.sleep-report .sr-head { display: flex; align-items: center; gap: var(--s3); font-size: var(--t-4); font-weight: 700; margin-bottom: var(--s2); }
.q-stars { display: inline-flex; gap: 2px; }
.q-stars .ico { font-size: 19px; }
.sr-head b { font-family: var(--font-display); font-size: var(--d-3); color: var(--gold); }
.sr-head .sub { font-size: var(--t-1); font-weight: 400; color: var(--text-dim); margin-left: auto; }
.sr-line { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); font-size: var(--t-2); color: var(--text-2); padding: var(--s2) 0 var(--s3); border-bottom: 1px solid var(--line-soft); font-variant-numeric: tabular-nums; }
.sr-line .ico { font-size: 14px; margin-right: 3px; }
.night-row { color: var(--text-2); font-size: var(--t-2); opacity: 0.86; }
.night-row .ico { font-size: 13px; color: #9db4ff; }
.sr-warn {
  display: flex; align-items: center; gap: var(--s2); margin-top: var(--s3);
  padding: var(--s2) var(--s3); font-size: var(--t-2); line-height: 1.5; color: #ffe6a8;
  background: rgba(255, 209, 102, 0.1); border-left: 3px solid var(--gold);
}
.sr-sick {
  display: flex; align-items: center; gap: var(--s2); margin-top: var(--s2);
  padding: var(--s2) var(--s3); font-size: var(--t-2); line-height: 1.5; color: #ffc9c9;
  background: rgba(255, 107, 107, 0.1); border-left: 3px solid var(--danger);
}
.sr-sick .pixel-btn { margin-left: auto; flex-shrink: 0; }

/* ---------- hospital: health is bought, never given ---------- */
.hosp-hint { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); }
.hosp-hint .ico { font-size: 13px; margin-right: 3px; }
.hosp-row {
  display: block; padding: var(--s3); margin-bottom: var(--s2);
  border: 1px solid var(--line-soft); background: rgba(255, 107, 107, 0.05);
}
.hosp-row:hover { transform: none; background: rgba(255, 107, 107, 0.08); }
.hosp-head { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-3); font-weight: 600; }
.hosp-head b { color: var(--text); }
.hosp-head .ico { font-size: 16px; color: #ff9f9f; }
.hosp-head em { font-style: normal; font-weight: 400; font-size: var(--t-1); color: var(--text-dim); }
.hosp-head .chronic { color: var(--gold); border: 1px solid rgba(255, 209, 102, 0.35); padding: 1px 5px; }
.sev { display: inline-flex; gap: 3px; margin-left: auto; }
.sev i { width: 7px; height: 7px; background: #2a2f52; display: inline-block; }
.sev i.on { background: var(--danger); }
.sev.max i.on { background: #ff2d55; box-shadow: 0 0 6px rgba(255, 45, 85, 0.6); }
.hosp-row .sub { margin: var(--s1) 0 var(--s2); }
.hosp-act { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); }
.hosp-cost { font-size: var(--t-1); color: var(--text-dim); margin-left: auto; font-variant-numeric: tabular-nums; }

/* ---------- scam: the world stops and asks ---------- */
.trap .trap-scene { font-size: var(--t-3); line-height: 1.6; color: var(--text); margin: 0 0 var(--s4); }
.trap-opts { display: grid; gap: var(--s2); }
.modal-content .trap-opt {
  display: flex; align-items: center; gap: var(--s3); width: 100%; text-align: left;
  background: linear-gradient(90deg, rgba(255, 74, 74, 0.1), rgba(255, 74, 74, 0.02));
  border: 1px solid var(--line-soft); border-left: 3px solid #ff6b6b;
  color: var(--text); padding: var(--s3) var(--s3); font: inherit; font-size: var(--t-2); cursor: pointer;
  transition: background 0.14s ease, transform 0.14s ease, border-color 0.14s ease;
}
.modal-content .trap-opt:hover { background: linear-gradient(90deg, rgba(255, 107, 107, 0.2), rgba(255, 107, 107, 0.05)); transform: translateX(2px); }
.modal-content .trap-opt:active { transform: translateX(2px) scale(0.995); }
.trap-opt .to-text { flex: 1; }
.trap-opt .tp-stat { display: inline-flex; align-items: center; gap: 3px; font-size: var(--t-1); color: var(--gold); white-space: nowrap; letter-spacing: 0.5px; }
.trap-opt .tp-stat .ico { font-size: 12px; }
.modal-content .trap { animation: trapShake 0.42s cubic-bezier(0.3, 0.9, 0.3, 1); }
@keyframes trapShake {
  0%, 100% { transform: translateX(0); } 18% { transform: translateX(-5px); }
  36% { transform: translateX(4px); } 58% { transform: translateX(-3px); } 78% { transform: translateX(2px); }
}

@media (prefers-reduced-motion: reduce) {
  .day-card .dc-frame, .day-card.out, .dock.in, .dock.in .dock-body, .modal.active .modal-content,
  .ticker-row, .log-entry, .notification, .sleep-overlay, .so-moon, .zzz span,
  .action-btn.deny, .hot-btn.deny { animation: none; }
}

/* pixel frame: chunky bevel used on every panel */
.pixel-border {
  border: 2px solid var(--line);
  background: linear-gradient(180deg, rgba(36, 40, 72, 0.92), rgba(14, 16, 34, 0.94));
  box-shadow:
    inset 1px 1px 0 rgba(255, 255, 255, 0.07),
    inset -1px -1px 0 rgba(0, 0, 0, 0.45);
}

/* ---------- screens ---------- */
.screen { display: none; width: 100vw; height: 100dvh; }
.screen.active { display: flex; flex-direction: column; }

/* ============================ LOGIN ============================ */
#loginScreen {
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(120% 80% at 50% 0%, #1d1140 0%, transparent 60%),
    radial-gradient(90% 70% at 80% 100%, #06263d 0%, transparent 55%),
    linear-gradient(135deg, #07071c 0%, #150a2e 55%, #061524 100%);
}

.login-box {
  padding: var(--s6) var(--s6) var(--s5);
  text-align: center;
  max-width: 440px;
  width: min(92vw, 440px);
  border-radius: var(--r);
  box-shadow: var(--shadow), 0 0 60px rgba(74, 158, 255, 0.12);
  animation: floatIn 0.5s ease-out;
}

@keyframes floatIn {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: translateY(0); }
}

/* The brand is two lines because it is two scripts. Chinese takes the body face at the top
   of the ladder and a single hard drop — a coloured offset on 像素 would fill in its own
   strokes and read as a stray line, which is exactly what the owner flagged. Latin takes the
   pixel face underneath it, where that font actually has glyphs. */
.game-title {
  font-size: var(--t-6);
  font-weight: 800;
  line-height: 1.3;
  letter-spacing: 8px;
  text-indent: 8px; /* the tracking is added after the last glyph too: without this the
                       word is not centred, it is centred plus one letter of slack */
  color: var(--gold);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.75);
}
.gt-en {
  font-family: var(--font-display);
  font-size: var(--d-2);
  letter-spacing: 3px;
  color: var(--highlight);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.55);
  margin-top: var(--s2);
}

.subtitle {
  font-size: var(--t-1);
  color: var(--text-dim);
  margin: var(--s3) 0 var(--s5);
  letter-spacing: 0.5px;
}

.login-form { display: flex; flex-direction: column; gap: var(--s2); }
.pixel-input {
  font-family: var(--font-body);
  font-size: var(--t-3);
  min-height: var(--ctl-md);
  padding: 0 var(--s3);
  background: var(--bg-void);
  color: var(--text);
  border: 2px solid var(--line);
  border-radius: var(--r);
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.pixel-input::placeholder { color: #585f8a; }
.pixel-input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.2); }

/* ---------- the key ----------
   A button is a physical thing in this game: it stands on a plinth, it is pushed down into
   it, and its label is always the same voice. The label is set in the body face on purpose —
   the pixel face has no Chinese in it, so a bilingual label used to be half one font and half
   another, and the half that was Chinese came out at 9px. The pixel identity lives in the
   frame instead, where it cannot fail to render. */
.pixel-btn {
  font-family: var(--font-body);
  font-size: var(--t-3);
  font-weight: 650;
  line-height: 1.15;
  letter-spacing: 0.01em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: var(--ctl-md);
  padding: 0 var(--s4);
  border: 1px solid rgba(0, 0, 0, 0.6);
  border-radius: var(--r-2);
  cursor: pointer;
  background: linear-gradient(180deg, #3b4173 0%, #2b3059 46%, #22264a 100%);
  color: var(--text);
  box-shadow: var(--key);
  transition: transform 0.07s steps(2, end), box-shadow 0.07s steps(2, end), filter 0.12s ease;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.pixel-btn:hover:not(:disabled) { filter: brightness(1.14); }
.pixel-btn:active:not(:disabled) { transform: translateY(3px); box-shadow: var(--key-flat); }
.pixel-btn.primary {
  background: linear-gradient(180deg, #ffdf94 0%, #f7c242 44%, #d99a1c 100%);
  border-color: #7a5410;
  color: #2a1d00;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35);
}
.pixel-btn.secondary {
  background: linear-gradient(180deg, #262b4c 0%, #1d2140 46%, #171a33 100%);
  color: var(--text-2);
}
.pixel-btn.small { font-size: var(--t-2); min-height: var(--ctl-sm); padding: 0 var(--s3); }
.pixel-btn.tiny { font-size: var(--t-1); min-height: 28px; padding: 0 var(--s2); border-width: 1px; white-space: nowrap; box-shadow: var(--key-flat); }
.pixel-btn.tiny:active:not(:disabled) { transform: translateY(1px); }
.pixel-btn.danger {
  background: linear-gradient(180deg, #e2635f 0%, #b0303a 46%, #7d1f2a 100%);
  border-color: #4b1017;
  color: #fff1f1;
}
.pixel-btn.wide { width: 100%; }
/* a disabled key is flat and drained rather than translucent: at 40% opacity on a dark plate
   the label was unreadable, which is the opposite of what a control that cannot be used
   should be — it should still say what it would have done */
.pixel-btn:disabled {
  opacity: 1;
  cursor: not-allowed;
  filter: saturate(0.28) brightness(0.72);
  box-shadow: var(--key-flat);
  color: var(--text-2);
}
/* toggle buttons carry a sprite that swaps between filled and hollow, so the
   button itself only needs a lit frame */
.pixel-btn.on {
  border-color: #6a5a10;
  background: linear-gradient(180deg, rgba(255, 209, 102, 0.26) 0%, rgba(255, 209, 102, 0.12) 46%, rgba(255, 209, 102, 0.06) 100%);
  color: var(--gold);
  box-shadow: inset 0 1px 0 rgba(255, 226, 150, 0.35), inset 0 -2px 0 rgba(0, 0, 0, 0.34), 0 3px 0 rgba(0, 0, 0, 0.45);
}
.list-item em.here { display: inline-flex; align-items: center; gap: 3px; color: var(--success); }

.error-msg {
  color: var(--danger);
  font-size: var(--t-1);
  margin-top: var(--s3);
  min-height: 1.2em;
}

.login-hint {
  font-size: var(--t-1);
  color: var(--text-dim);
  margin-top: var(--s4);
  line-height: 1.7;
}

/* ============================ CHARACTER CREATION ============================
   One screen, three decisions, and the city list has to be readable on a phone
   held at arm's length: the card carries four numbers, not a paragraph. */
#createScreen {
  align-items: center;
  justify-content: center;
  padding: var(--s3);
  background:
    radial-gradient(120% 80% at 50% 0%, #1d1140 0%, transparent 60%),
    radial-gradient(90% 70% at 80% 100%, #06263d 0%, transparent 55%),
    linear-gradient(135deg, #07071c 0%, #150a2e 55%, #061524 100%);
}

.create-card {
  width: min(1180px, 100%);
  max-height: calc(100dvh - var(--s3) * 2);
  display: flex;
  flex-direction: column;
  border-radius: var(--r);
  box-shadow: var(--shadow), 0 0 60px rgba(74, 158, 255, 0.1);
  animation: floatIn 0.4s ease-out;
}

.create-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: var(--s5) var(--s5) var(--s3);
}

.create-title {
  font-size: var(--t-5);
  font-weight: 800;
  line-height: 1.4;
  letter-spacing: 3px;
  color: var(--gold);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.7);
}
.create-title em { font-family: var(--font-display); font-style: normal; color: var(--text-2); text-shadow: none; font-size: var(--d-2); letter-spacing: 1px; margin-left: 10px; }

.create-sub { font-size: var(--t-2); color: var(--text-dim); line-height: 1.6; }

.create-body { flex: 1; overflow-y: auto; padding: 0 var(--s5) var(--s3); -webkit-overflow-scrolling: touch; }

.create-sec { padding: var(--s3) 0; border-top: 1px solid var(--line-soft); }
.create-sec:first-child { border-top: 0; padding-top: var(--s2); }
.create-sec[hidden] { display: none; }
/* a text input with no width is a browser-default 180px box: on the one screen where
   the name is the whole point, that reads as a leftover form field */
.create-sec .pixel-input { width: 100%; max-width: 420px; min-height: var(--ctl-lg); font-size: var(--t-4); padding: 0 var(--s3); }

.create-label {
  font-size: var(--t-1);
  font-weight: 700;
  letter-spacing: 1.5px;
  color: var(--accent-2);
  margin-bottom: 4px;
}
.create-hint { font-size: var(--t-2); color: var(--text-dim); line-height: 1.6; margin-bottom: var(--s2); max-width: 760px; }

.cc-gender { display: flex; gap: var(--s2); flex-wrap: wrap; }
.cc-g {
  /* flex-grow with a ceiling: two half-width banners look like a settings row on a
     desktop and a phone alike, while a phone still gets the full width */
  flex: 1 1 150px;
  max-width: 260px;
  min-height: 58px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: var(--s2);
  background: var(--bg-light);
  border: 2px solid var(--line);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.1s ease;
}
.cc-g b { font-size: var(--t-3); font-weight: 700; }
.cc-g i { font-style: normal; font-size: var(--t-1); color: var(--text-dim); }
.cc-g:hover { transform: translateY(-1px); }
.cc-g.on { border-color: var(--gold); background: rgba(255, 209, 102, 0.13); color: var(--gold); }
.cc-g.on i { color: var(--gold-solid); }

.city-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--s2);
}

.ccard {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  text-align: left;
  padding: 10px 12px;
  background: var(--bg-light);
  border: 2px solid var(--line-soft);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.1s ease;
}
.ccard:hover { transform: translateY(-1px); border-color: var(--line); }
.ccard.on { border-color: var(--gold); background: rgba(255, 209, 102, 0.1); box-shadow: inset 0 0 0 1px rgba(255, 209, 102, 0.25); }

.cc-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; width: 100%; }
.cc-name { font-size: var(--t-4); font-weight: 800; letter-spacing: 0.5px; }
.cc-country { font-style: normal; font-size: var(--t-1); color: var(--text-dim); }
.cc-top .cb-stage { margin-left: auto; font-size: var(--t-1); }
.cc-open { font-size: var(--t-1); color: var(--text-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }

.cc-metrics { display: flex; flex-wrap: wrap; gap: 4px 10px; width: 100%; }
.cc-m { display: inline-flex; align-items: baseline; gap: 4px; font-size: var(--t-2); }
.cc-m i { font-style: normal; color: var(--text-dim); }
.cc-m b { font-variant-numeric: tabular-nums; font-weight: 700; }
.cc-m.good b { color: var(--success); }
.cc-m.warn b { color: var(--gold); }
.cc-m.bad b { color: var(--danger); }

/* the seat line is the whole argument of the card — what you can get hired into today and
   what is left at the end of the month — so it is the one line in the card set at reading
   size with weight behind it, not at footnote size */
.cc-seat { font-size: var(--t-2); color: var(--text-2); font-variant-numeric: tabular-nums; }
.cc-seat b { color: var(--text); font-weight: 700; }
.cc-seat.dim { color: var(--text-dim); }
.cc-lang { font-size: var(--t-1); color: var(--text-dim); line-height: 1.5; }
.cc-lang.down { color: var(--gold); }
.cc-desc { font-size: var(--t-1); color: var(--text-dim); opacity: 0.85; line-height: 1.6; }

.create-foot {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s5) var(--s4);
  border-top: 1px solid var(--line);
  background: rgba(5, 6, 15, 0.5);
  border-radius: 0 0 var(--r) var(--r);
}
.create-note { flex: 1; font-size: var(--t-2); line-height: 1.55; color: var(--text-2); }
.create-note.dim { color: var(--text-dim); }
.create-note b { color: var(--text); font-variant-numeric: tabular-nums; }
.create-warn { color: var(--gold); margin-top: 2px; }
.create-foot .pixel-btn { flex: 0 0 auto; min-height: var(--ctl-lg); padding: 0 var(--s5); }

@media (max-width: 720px) {
  .create-card { max-height: 100dvh; height: 100dvh; border-radius: 0; }
  #createScreen { padding: 0; }
  .create-head { padding: var(--s4) var(--s3) var(--s2); }
  .create-title { font-size: var(--t-5); letter-spacing: 2px; }
  .create-title em { display: block; margin-left: 0; }
  .create-body { padding: 0 var(--s3) var(--s2); }
  .city-grid { grid-template-columns: 1fr; }
  .create-foot { padding: var(--s2) var(--s3) calc(var(--s3) + env(safe-area-inset-bottom)); }
  .create-foot .pixel-btn { padding: 0 var(--s4); }
  .cc-g { max-width: none; }
}

/* ============================ HUD ============================ */
#gameScreen { background: var(--bg-void); }

.hud {
  flex: 0 0 var(--hud-h);
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s3);
  border-left: 0; border-right: 0; border-top: 0;
  background: linear-gradient(180deg, #1a1d38, #0c0e20);
  overflow: hidden;
}

.hud-group {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 0 var(--s3);
  height: 100%;
  border-right: 1px solid var(--line-soft);
  flex: 0 0 auto;
}
.hud-group:last-child { border-right: 0; }

.hud-label {
  font-size: var(--t-1);
  color: var(--text-dim);
  letter-spacing: 1px;
}
.hud-value {
  font-size: var(--t-2);
  font-weight: 700;
  color: var(--gold);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.hud-value.big { font-size: var(--t-4); font-weight: 800; color: var(--success); }
.hud-unit { font-size: var(--t-1); color: var(--text-dim); }
.hud-sub {
  font-size: var(--t-1);
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 190px;
}
.sep { display: inline-block; width: 6px; }
/* the watch line is two counts, each fronted by a glyph. An icon is a 1em box with the
   digit pressed against its edge, so 指数 sub read as one smeared "0⌁0" — the pair needs
   air inside it and more air between the pairs */
.hud-sub .ico { margin-right: 3px; }
.hud-sub .ico ~ .ico { margin-left: var(--s3); }

.hud-group.id { padding-left: 0; }
.id-name { font-size: var(--t-3); font-weight: 700; color: var(--text); line-height: 1.2; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.id-meta { display: flex; align-items: baseline; gap: 3px; }

.hud-group.place .hud-value { font-size: var(--t-3); }

.hud-group.vitals { gap: 4px; padding-top: 6px; padding-bottom: 6px; min-width: 250px; }
.bar-row { display: flex; align-items: center; gap: var(--s1); }
/* two ideographs and one English word have to occupy the same column or every bar in the
   strip starts at a different x — hence a fixed width with the label clipped, not a shrink-wrapped one */
.bar-name { font-size: var(--t-1); color: var(--text-dim); width: 40px; white-space: nowrap; overflow: hidden; }
.bar-val { font-family: var(--font-display); font-size: var(--d-2); color: var(--text); width: 40px; text-align: right; }
.stat-bar {
  flex: 1;
  height: 9px;
  min-width: 90px;
  background: #0a0c1c;
  border: 1px solid #333a63;
  overflow: hidden;
}
.stat-fill { height: 100%; width: 0; transition: width 0.45s cubic-bezier(0.2, 0.8, 0.2, 1); }
.stat-bar.health .stat-fill { background: linear-gradient(90deg, #d94a5a, var(--success)); }
.stat-bar.happy .stat-fill { background: linear-gradient(90deg, var(--highlight), var(--gold)); }
.stat-bar.energy .stat-fill { background: linear-gradient(90deg, #2a6aaa, var(--accent-2)); }
.stat-bar.intimacy { height: 6px; margin-top: var(--s2); }
.stat-bar.intimacy .stat-fill { background: linear-gradient(90deg, #9f6dff, #ff77aa); }

.hud-group.wealth { min-width: 240px; }
.wealth-main { display: flex; align-items: baseline; gap: var(--s2); }
.wealth-sub { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3); font-size: var(--t-1); color: var(--text-dim); }
.wealth-sub span { display: inline-flex; gap: 4px; align-items: baseline; }
.wealth-sub .k { font-style: normal; color: #5f6690; }
.wealth-sub b { font-weight: 600; color: var(--text-2); font-variant-numeric: tabular-nums; }
.wealth-sub b.bad, .hud-value.bad { color: var(--danger); }

/* the clock is the game's heartbeat: it gets the second slot and the largest
   numerals after the player's own name. It is also the one HUD value that is never
   anything but digits, so it is the one that keeps the pixel face. */
.hud-group.time { min-width: 132px; }
.clock-row { display: flex; align-items: center; gap: var(--s2); }
.hud-value.clock { font-family: var(--font-display); font-size: var(--d-3); color: var(--gold); letter-spacing: 0; font-variant-numeric: tabular-nums; }
.hud-value.clock.after-dark { color: #9fb6ff; }
.clock-weather { font-size: 16px; }
.hud-sub.debt-warn { color: var(--danger); }

/* ============================ STAGE ============================ */
.stage {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: var(--rail-w) 1fr;
}

/* --- icon rail --- */
.rail {
  display: flex;
  flex-direction: column;
  padding: var(--s2) 0;
  border-top: 0;
  border-left: 0;
  border-bottom: 0;
  overflow: hidden;
  background: linear-gradient(180deg, #141733, #0a0c1c);
}
/* the module list scrolls on its own so the account controls stay reachable
   at the bottom of the column instead of being pushed out of view */
.rail-scroll {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
  overflow-x: hidden;
}
.rail-foot { flex: 0 0 auto; display: flex; flex-direction: column; gap: 2px; padding-bottom: var(--s1); }
.rail-foot #rail-logout:hover { background: rgba(231, 76, 60, 0.14); color: var(--danger); }
.rail-sep { height: 1px; margin: var(--s2) var(--s3); background: var(--line-soft); flex: 0 0 auto; }
.rail-btn {
  flex: 0 0 auto;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 100%;
  height: 54px;
  background: transparent;
  border: 0;
  border-left: 3px solid transparent;
  color: var(--text-dim);
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.rail-btn:hover { background: rgba(74, 158, 255, 0.1); color: var(--text); }
.rail-btn.active { background: rgba(74, 158, 255, 0.16); color: var(--gold); border-left-color: var(--gold); }
.rail-icon { font-size: 18px; line-height: 1; filter: saturate(0.85); }
.rail-btn.active .rail-icon { filter: none; }
/* the rail is 68px wide, so its label budget is two ideographs. Anything longer is the
   confirm state, and that one gets a shortened phrase rather than a clipped one. */
.rail-label { font-size: var(--t-1); line-height: 1.25; max-width: 64px; white-space: nowrap; }
.rail-badge {
  position: absolute;
  top: 8px;
  right: 12px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--highlight);
  color: #fff;
  font-family: var(--font-display);
  font-size: var(--d-1);
  line-height: 16px;
  text-align: center;
}

/* --- world --- */
.world {
  position: relative;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: #06070f;
}
#gameCanvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.canvas-overlay { position: absolute; inset: 0; pointer-events: none; }

/* ============================ DOCK ============================ */
.dock {
  position: absolute;
  top: var(--s3);
  right: var(--s3);
  bottom: calc(var(--bar-h) + var(--s4));
  width: var(--dock-w);
  max-width: calc(100% - var(--s6));
  display: flex;
  flex-direction: column;
  border-radius: var(--r);
  background: rgba(10, 12, 26, 0.94);
  backdrop-filter: blur(8px);
  box-shadow: var(--shadow);
  z-index: 30;
  animation: dockIn 0.18s ease-out;
}
.dock[hidden] { display: none; }
@keyframes dockIn {
  from { opacity: 0; transform: translateX(24px); }
  to { opacity: 1; transform: none; }
}
.dock-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s3);
  border-bottom: 2px solid var(--line);
  background: linear-gradient(180deg, #1d2140, #131630);
}
.dock-icon { font-size: 18px; }
.dock-title { flex: 1; font-size: var(--t-4); font-weight: 800; letter-spacing: 1px; color: var(--gold); }
.dock-close {
  width: 30px; height: 30px;
  background: var(--bg-light);
  border: 2px solid var(--line);
  color: var(--text-dim);
  cursor: pointer;
  font-size: var(--t-2);
  line-height: 1;
}
.dock-close:hover { color: var(--danger); border-color: var(--danger); }
.dock-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--s3);
  scrollbar-width: thin;
}

/* ============================ ACTION BAR ============================ */
.bar {
  position: absolute;
  left: var(--s3);
  right: var(--s3);
  bottom: var(--s3);
  height: var(--bar-h);
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s3);
  border-radius: var(--r);
  background: rgba(12, 14, 32, 0.92);
  backdrop-filter: blur(8px);
  z-index: 25;
}
.hotbar { display: flex; align-items: center; gap: var(--s2); flex: 1; min-width: 0; overflow-x: auto; }
.hot-btn {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--s2);
  height: 52px;
  padding: 0 var(--s3) 0 var(--s2);
  background: linear-gradient(180deg, #343b6b 0%, #262b52 46%, #1c2040 100%);
  border: 1px solid rgba(0, 0, 0, 0.6);
  border-radius: var(--r-2);
  box-shadow: var(--key);
  cursor: pointer;
  transition: transform 0.07s steps(2, end), box-shadow 0.07s steps(2, end), filter 0.12s ease;
}
.hot-btn:hover:not(:disabled) { filter: brightness(1.14); }
.hot-btn:active:not(:disabled) { transform: translateY(3px); box-shadow: var(--key-flat); }
/* same rule as every other key in the game: a spent action still has to say what it was */
.hot-btn:disabled { cursor: not-allowed; filter: saturate(0.28) brightness(0.72); box-shadow: var(--key-flat); }
.hot-icon { font-size: 20px; line-height: 1; }
.hot-text { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; }
.hot-label { font-size: var(--t-2); font-weight: 700; color: var(--text); white-space: nowrap; }
.hot-cost { font-size: var(--t-1); color: var(--text-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
.hot-key {
  position: absolute;
  top: -1px; left: -1px;
  font-family: var(--font-display);
  font-size: var(--d-1);
  padding: 2px 3px;
  background: var(--accent);
  color: #04122b;
}
.more-btn {
  flex: 0 0 auto;
  width: 44px; height: 52px;
  background: linear-gradient(180deg, #2a2f57 0%, #1d2140 46%, #141830 100%);
  border: 1px solid rgba(0, 0, 0, 0.6);
  border-radius: var(--r-2);
  box-shadow: var(--key);
  color: var(--text-2);
  font-size: var(--t-5);
  line-height: 1;
  cursor: pointer;
  transition: transform 0.07s steps(2, end), box-shadow 0.07s steps(2, end), filter 0.12s ease;
}
.more-btn:hover { filter: brightness(1.18); color: var(--gold); }
.more-btn:active { transform: translateY(3px); box-shadow: var(--key-flat); }

.bar-right { flex: 0 0 auto; display: flex; align-items: center; gap: var(--s3); margin-left: auto; }
.status-chips { display: flex; gap: var(--s2); }
.chip-warn {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--t-1); font-weight: 600; line-height: 1; padding: 8px 10px; border-radius: var(--r-2);
  border: 1px solid rgba(231, 76, 60, 0.55); color: #ffb4a8; background: rgba(231, 76, 60, 0.16);
  box-shadow: var(--key-flat);
  animation: chipPulse 1.9s ease-in-out infinite;
}
.chip-warn .ico { font-size: var(--t-3); }
.chip-warn.sick { border-color: rgba(255, 107, 107, 0.6); color: #ffc9c9; background: rgba(255, 107, 107, 0.16); cursor: pointer; }
.chip-warn.tired { border-color: rgba(255, 209, 102, 0.45); color: #ffe6a8; background: rgba(255, 209, 102, 0.12); }
.chip-warn.resting { border-color: rgba(126, 224, 168, 0.45); color: #bdf2d4; background: rgba(126, 224, 168, 0.12); animation: none; }
@keyframes chipPulse { 0%, 100% { box-shadow: 0 0 0 rgba(255, 209, 102, 0); } 50% { box-shadow: 0 0 12px rgba(255, 209, 102, 0.35); } }

/* sleep is the day's real ending, so it reads as the one big night key: the same plinth as
   every other control, a label line in the reading voice and the plan under it in the meta
   voice. The plan used to be 9px pixel type inside a 226px box, which is how "09:29-16:59"
   ended in an ellipsis on a phone. */
.sleep-btn {
  display: flex;
  align-items: center;
  gap: var(--s2);
  height: 56px;
  padding: 0 var(--s4);
  font-family: var(--font-body);
  color: #dfe6ff;
  background: linear-gradient(180deg, #4a5196 0%, #2c3163 46%, #1e2246 100%);
  border: 1px solid rgba(0, 0, 0, 0.6);
  border-radius: var(--r-2);
  box-shadow: var(--key);
  cursor: pointer;
  transition: transform 0.07s steps(2, end), box-shadow 0.07s steps(2, end), filter 0.12s ease;
}
.sleep-btn:hover { filter: brightness(1.12); }
.sleep-btn:active { transform: translateY(3px); box-shadow: var(--key-flat); }
.sleep-btn.urgent { border-color: #7a5410; background: linear-gradient(180deg, #ffdf94 0%, #f7c242 44%, #d99a1c 100%); color: #2a1d00; animation: sleepCall 1.6s ease-in-out infinite; }
/* the day belongs to the employer: same box, same size, a steel palette and no
   glow, so the button never reads as an invitation to lie down */
.sleep-btn.on-duty { border-color: #10222c; background: linear-gradient(180deg, #46708c 0%, #26414f 46%, #16242e 100%); color: #eaf4fa; }
.sleep-btn.on-duty:hover { filter: brightness(1.08); }
.sleep-btn.urgent kbd { color: #3a2a00; background: rgba(0, 0, 0, 0.2); }
.sleep-btn.urgent .sleep-text em { opacity: 0.72; }
@keyframes sleepCall { 0%, 100% { box-shadow: var(--key), 0 0 0 rgba(255, 209, 102, 0); } 50% { box-shadow: var(--key), 0 0 18px rgba(255, 209, 102, 0.55); } }
/* min-width, not width: the box is wide enough for the longest plan the clock can write,
   and a shorter one leaves it alone rather than clipping it */
.sleep-text { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 150px; }
.sleep-text > span { font-size: var(--t-3); font-weight: 700; line-height: 1.1; letter-spacing: 1px; }
.sleep-text em { font-style: normal; font-size: var(--t-1); line-height: 1.2; opacity: 0.82; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sleep-icon { font-size: 20px; }
.sleep-btn kbd {
  font-family: var(--font-display);
  font-size: var(--d-1);
  padding: 3px 4px;
  background: rgba(0, 0, 0, 0.28);
  border-radius: 2px;
  color: #cdd6ff;
}

/* ============================ LOG TICKER ============================ */
.ticker {
  position: absolute;
  left: var(--s3);
  bottom: calc(var(--bar-h) + var(--s4));
  width: min(560px, 58%);
  display: flex;
  flex-direction: column;
  gap: 3px;
  pointer-events: none;
  z-index: 20;
}
.ticker-row {
  font-size: var(--t-2);
  line-height: 1.45;
  padding: 4px var(--s2);
  background: rgba(6, 8, 20, 0.78);
  border-left: 3px solid var(--accent);
  color: var(--text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  animation: tickerIn 0.25s ease-out;
}
.ticker-row .tk-day { font-family: var(--font-display); font-size: var(--d-1); color: #5a6191; margin-right: 6px; }
.ticker-row.good { border-left-color: var(--success); color: #b6f2ce; }
.ticker-row.bad { border-left-color: var(--danger); color: #ffc3c3; }
.ticker-row.special { border-left-color: var(--gold); color: #ffeab0; }
.ticker-row.event { border-left-color: #ff66ff; color: #f3c9ff; }
@keyframes tickerIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ============================ CONTENT ATOMS ============================ */
.pane-hint {
  font-size: var(--t-2);
  color: var(--text-dim);
  line-height: 1.65;
  margin-bottom: var(--s3);
}
.pane-hint + .action-list { margin-top: var(--s2); }
.empty {
  font-size: var(--t-2);
  color: var(--text-dim);
  padding: var(--s5) var(--s3);
  text-align: center;
  line-height: 1.7;
}
.hint-right { float: right; font-size: var(--t-1); font-weight: 400; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.up { color: var(--success); }
.down { color: var(--danger); }
.dim { opacity: 0.45; }
.good-txt, .city-info .good { color: var(--success); }

.section-title {
  font-size: var(--t-3);
  font-weight: 700;
  color: var(--text-2);
  letter-spacing: 1px;
  margin: var(--s4) 0 var(--s2);
  padding-bottom: var(--s1);
  border-bottom: 1px solid var(--line-soft);
}

/* --- action buttons (dock lists) --- */
.action-list { display: flex; flex-direction: column; gap: var(--s2); margin-bottom: var(--s3); }
.action-btn {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex-wrap: wrap;
  position: relative;
  width: 100%;
  padding: 10px var(--s3);
  text-align: left;
  background: linear-gradient(180deg, #1d2242, #161a34);
  color: var(--text);
  border: 1px solid #333a63;
  border-left: 3px solid var(--accent);
  border-radius: var(--r);
  cursor: pointer;
  transition: transform 0.08s, border-color 0.12s, background 0.12s;
}
.action-btn:hover:not(:disabled) { transform: translateX(2px); border-left-color: var(--gold); background: linear-gradient(180deg, #242b52, #1a1e3c); }
.action-btn:disabled { cursor: not-allowed; filter: saturate(0.35) brightness(0.82); color: var(--text-dim); }
.action-btn .action-icon { font-size: 20px; line-height: 1; }
.action-btn .action-text { flex: 1; font-size: var(--t-3); font-weight: 700; color: inherit; }
.action-btn .action-detail { font-size: var(--t-1); color: var(--text-dim); font-variant-numeric: tabular-nums; }
.action-btn.locked { border-left-color: #4a4f6e; }
/* a locked row is still a question you can ask: clicking it answers with the reason */
.action-btn.locked { cursor: help; }
.action-btn.locked:hover { transform: none; border-left-color: var(--danger); background: linear-gradient(180deg, #2b2140, #1a1e3c); }
@keyframes denyShake {
  0%, 100% { transform: translateX(0); }
  18% { transform: translateX(-4px); }
  42% { transform: translateX(3px); }
  66% { transform: translateX(-2px); }
  85% { transform: translateX(1px); }
}
.action-btn.deny, .hot-btn.deny { animation: denyShake 0.34s ease-out; border-left-color: var(--danger); }
.modal.active .modal-content.deny { animation: denyShake 0.34s ease-out; border-color: var(--danger); }
.hot-btn.locked { cursor: help; filter: saturate(0.45) brightness(0.85); }
.action-btn.decision { border-left-color: var(--purple); }
.action-btn.study { border-left-color: var(--accent-2); }
.action-btn.stock { border-left-color: #ff9f5a; }
.action-btn.property { border-left-color: #aa66ff; }
.action-btn.loan { border-left-color: var(--gold); }
.action-btn.shop { border-left-color: var(--highlight); }
.action-btn.travel { border-left-color: var(--accent-2); }
.action-btn.invest { border-left-color: var(--accent); }
.action-btn.next { border-left-color: var(--gold); }
.action-btn.career { border-left-color: var(--accent); }
.action-btn.career.current { border-left-color: var(--success); background: rgba(74, 222, 128, 0.1); }
.action-btn.danger { border-left-color: var(--danger); }
.lock-note { width: 100%; font-size: var(--t-1); color: #ffa58c; line-height: 1.5; margin-top: 2px; }
.lock-note.ok { color: var(--accent); }
.lock-note.done { color: var(--success); }

/* the card that answers a locked control: one readable row per missing number */
.lock-card { display: flex; flex-direction: column; gap: var(--s2); }
.lock-row {
  display: flex;
  align-items: flex-start;
  gap: var(--s2);
  padding: 9px var(--s3);
  border: 1px solid rgba(255, 138, 110, 0.32);
  border-left: 3px solid var(--danger);
  border-radius: var(--r);
  background: rgba(34, 8, 12, 0.55);
  font-size: 13px;
  line-height: 1.55;
  color: #ffd7cd;
}
.lock-row .ico { font-size: 14px; flex: 0 0 auto; }

/* --- generic list rows --- */
.list-item {
  padding: 10px var(--s3);
  margin-bottom: var(--s2);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--line-soft);
  border-radius: var(--r);
  font-size: var(--t-2);
  transition: background 0.12s, border-color 0.12s;
}
.list-item:hover { background: rgba(74, 158, 255, 0.08); }
.list-item.row { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); cursor: pointer; }
.list-item.mini { padding: var(--s2) 10px; font-size: var(--t-2); }
.list-item .row-right { display: flex; align-items: center; gap: var(--s2); flex-shrink: 0; }
.list-item .price { font-size: var(--t-2); color: var(--success); text-align: right; font-variant-numeric: tabular-nums; }
.list-item .sub { font-size: var(--t-1); color: var(--text-dim); margin-top: 2px; line-height: 1.55; font-variant-numeric: tabular-nums; }
.list-item .qty { font-family: var(--font-display); font-size: var(--d-1); color: var(--gold); }
.list-item em { font-style: normal; color: var(--text-dim); font-size: var(--t-1); }
.list-item .up, .list-item .down { font-variant-numeric: tabular-nums; font-size: var(--t-2); }
.list-item.ach { font-size: var(--t-2); color: var(--text-dim); }
.list-item.ach.got { color: var(--gold); border-color: #6a5a10; background: rgba(255, 209, 102, 0.07); }
.list-item.mkt-row { align-items: flex-start; }
.mkt-name { font-size: var(--t-2); color: var(--text); }
/* a ticker code is the one string in the market list that is never anything but Latin, so it
   keeps the pixel face rather than a system monospace — and it is the code the player reads
   back to a friend, so it should look like the game drew it */
.tid { font-family: var(--font-display); font-size: var(--d-1); color: var(--text-dim); font-style: normal; }

.rel-head { display: flex; justify-content: space-between; align-items: center; gap: var(--s2); }
.rel-sub { font-size: var(--t-1); color: var(--text-dim); margin-top: var(--s1); }
.dot { display: inline-block; width: 8px; height: 8px; margin-right: var(--s1); border-radius: 2px; }

/* --- city info --- */
.city-info { font-size: var(--t-2); line-height: 1.7; color: var(--text-2); }
.city-info .city-name { font-size: var(--t-4); font-weight: 800; letter-spacing: 1px; color: var(--gold); margin-bottom: var(--s2); }
.city-info .city-country { font-size: var(--t-1); color: var(--text-dim); }
.city-info .city-desc { font-size: var(--t-2); color: var(--text-dim); margin-bottom: var(--s3); line-height: 1.7; }
.city-info .stat-line, .epitaph .stat-line {
  display: flex;
  justify-content: space-between;
  gap: var(--s2);
  padding: 6px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: var(--t-2);
}
.city-info .stat-line span:last-child, .epitaph .stat-line span:last-child { color: var(--text); font-variant-numeric: tabular-nums; }
.epitaph .stat-line { color: var(--text-dim); }
.skill-strip { display: flex; flex-wrap: wrap; gap: var(--s1); margin-top: var(--s3); }
.skill-chip {
  font-size: var(--t-1);
  padding: 4px 8px;
  background: rgba(74, 158, 255, 0.12);
  border: 1px solid #33507a;
  border-radius: 2px;
  color: var(--text-2);
}
.skill-chip b { color: var(--gold); font-variant-numeric: tabular-nums; }

/* --- market blocks --- */
.mkt-summary { padding: var(--s3); background: rgba(0, 0, 0, 0.35); border: 1px solid var(--line); border-radius: var(--r); margin-bottom: var(--s3); }
.mkt-idx { font-size: var(--t-3); font-weight: 700; color: var(--gold); font-variant-numeric: tabular-nums; }
.mkt-count, .mkt-meta { font-size: var(--t-1); color: var(--text-dim); margin-top: var(--s1); }
.mkt-head { margin-bottom: var(--s3); }
.mkt-index { font-size: var(--t-3); font-weight: 700; color: var(--gold); font-variant-numeric: tabular-nums; }
.mini-row {
  display: flex;
  justify-content: space-between;
  gap: var(--s2);
  font-size: var(--t-2);
  padding: 7px var(--s2);
  border-bottom: 1px solid var(--line-soft);
  cursor: pointer;
}
.mini-row:hover { background: rgba(74, 158, 255, 0.12); }
.mini-row span:last-child { font-variant-numeric: tabular-nums; }

.chip-row { display: flex; flex-wrap: wrap; gap: var(--s1); margin: var(--s2) 0; }
.chip {
  font-size: var(--t-1);
  font-weight: 600;
  min-height: 30px;
  padding: 0 var(--s3);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--bg-light);
  color: var(--text-2);
  border: 1px solid #3a3a5c;
  border-radius: var(--r-2);
  cursor: pointer;
}
.chip:hover { color: var(--text); border-color: var(--accent); }
.chip.on { background: var(--accent); color: #fff; border-color: var(--accent-2); }

.tiny-input { width: 100%; min-height: var(--ctl-sm); font-size: var(--t-2); margin-top: var(--s2); }
.pixel-input.tiny { width: 96px; min-height: var(--ctl-sm); font-size: var(--t-1); padding: 0 var(--s2); }
.pager { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); font-size: var(--t-1); color: var(--text-dim); margin-top: var(--s3); }

.co-head { margin-bottom: var(--s3); }
.co-price { font-family: var(--font-display); font-size: var(--d-3); color: var(--gold); display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.co-price em, .co-sub { font-family: var(--font-body); font-size: var(--t-1); color: var(--text-dim); font-style: normal; }
.co-sub { margin-top: var(--s1); font-size: var(--t-2); line-height: 1.6; }
.co-actions { display: flex; gap: var(--s2); margin-bottom: var(--s3); flex-wrap: wrap; }
.spark { width: 100%; height: 96px; display: block; border: 1px solid var(--line); background: #08081a; margin: var(--s2) 0; }

.fund-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s1); margin-bottom: var(--s3); }
.fund-grid div { background: rgba(0, 0, 0, 0.3); border: 1px solid var(--line); padding: var(--s2); border-radius: 2px; }
.fund-grid span { display: block; font-size: var(--t-1); color: var(--text-dim); }
.fund-grid b { display: block; color: var(--text); font-size: var(--t-2); font-weight: 700; font-variant-numeric: tabular-nums; }

.trade-box { padding: var(--s3); background: rgba(74, 158, 255, 0.07); border: 1px solid #33507a; border-radius: var(--r); display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s3); }
.trade-box.private { background: rgba(170, 102, 255, 0.08); border-color: #5c3a8a; }
.trade-box .sub { width: 100%; font-size: var(--t-1); color: var(--text-dim); line-height: 1.6; }

.stock-row .stock-head { display: flex; align-items: baseline; gap: var(--s2); font-size: 13px; flex-wrap: wrap; }
.stock-row .sym { font-family: var(--font-display); font-size: 9px; color: var(--gold); }
.stock-row .price { margin-left: auto; color: var(--success); font-variant-numeric: tabular-nums; }
.stock-row .vol { font-size: 11px; color: var(--text-dim); }
.stock-row .stock-held { font-size: 11.5px; color: var(--text-dim); margin: var(--s2) 0; }
.stock-row .stock-actions { display: flex; gap: 6px; align-items: center; }

/* --- news --- */
.news-item {
  padding: var(--s2) 10px;
  margin-bottom: 6px;
  background: rgba(0, 0, 0, 0.3);
  border-left: 3px solid var(--accent);
  border-radius: 2px;
  font-size: 12.5px;
  line-height: 1.55;
  cursor: pointer;
}
.news-item:hover { background: rgba(74, 158, 255, 0.1); }
.news-item.good { border-left-color: var(--success); }
.news-item.bad { border-left-color: var(--danger); }
.news-item.info { border-left-color: var(--accent); }
.news-sub { font-size: var(--t-1); color: var(--text-dim); margin-top: 2px; font-variant-numeric: tabular-nums; }
.news-line { font-size: var(--t-2); padding: 6px var(--s2); margin-bottom: var(--s1); background: rgba(0, 0, 0, 0.3); border-left: 3px solid var(--accent); }
.news-line.good { border-left-color: var(--success); }
.news-line.bad { border-left-color: var(--danger); }

/* --- log module --- */
.log-container { display: flex; flex-direction: column; gap: 6px; }
.log-entry {
  padding: var(--s2) 10px;
  background: rgba(255, 255, 255, 0.03);
  border-left: 3px solid var(--accent);
  border-radius: 2px;
  font-size: var(--t-2);
  line-height: 1.65;
  color: var(--text-2);
  animation: logSlide 0.25s ease-out;
}
@keyframes logSlide { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
.log-entry.good { border-left-color: var(--success); }
.log-entry.bad { border-left-color: var(--danger); }
.log-entry.special { border-left-color: var(--gold); color: #ffeab0; }
.log-entry.event { border-left-color: #ff66ff; }
.log-day { font-size: var(--t-1); color: #5a6191; margin-bottom: 2px; }

/* ============================ MODALS ============================ */
.modal {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(3, 4, 12, 0.82);
  backdrop-filter: blur(3px);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  padding: var(--s4);
}
.modal.active { display: flex; }
.modal-content {
  padding: var(--s5);
  max-width: 520px;
  width: 100%;
  max-height: 82dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: var(--r);
  box-shadow: var(--shadow);
  animation: modalPop 0.16s ease-out;
}
.modal-content.wide { max-width: 900px; }
@keyframes modalPop { from { transform: scale(0.96); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.modal-top { display: flex; align-items: flex-start; gap: var(--s3); flex: 0 0 auto; }
.modal-header {
  flex: 1 1 auto;
  font-size: var(--t-4);
  font-weight: 800;
  letter-spacing: 1px;
  line-height: 1.5;
  color: var(--gold);
  margin-bottom: var(--s4);
  text-align: center;
}
/* the close mark sits in a fixed header so a long panel can never hide it */
.modal-x {
  flex: 0 0 auto;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  background: rgba(12, 16, 34, 0.8);
  border: 2px solid var(--line);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color 0.14s ease, background 0.14s ease, transform 0.14s ease;
}
.modal-x:hover { border-color: var(--danger); background: rgba(50, 12, 18, 0.85); transform: translateY(-1px); }
.modal-x .ico { font-size: 15px; }
.modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; margin-bottom: var(--s4); padding-right: 3px; }
.modal-foot { flex: 0 0 auto; }
.modal-foot .pixel-btn.primary { width: 100%; }
.modal-body .pixel-btn.primary:last-child { width: 100%; }
/* a decision you have to make cannot be walked away from by clicking the dark */
.modal-content.required .modal-x, .modal-content.required .modal-foot { display: none; }

.death .modal-header { color: var(--danger); }

/* --- job posting card: pay, requirements, honest odds, what it costs --- */
.job-card .jc-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  padding: 7px var(--s3); margin-bottom: var(--s2);
  background: rgba(255, 255, 255, 0.03); border: 1px solid var(--line-soft); border-radius: var(--r);
  font-size: var(--t-2);
}
.job-card .jc-row b { font-variant-numeric: tabular-nums; }
.job-card .jc-req {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: var(--t-1); color: var(--success);
  padding: 7px var(--s3); margin-bottom: var(--s3);
  border-left: 3px solid var(--success); background: rgba(82, 185, 107, 0.09); border-radius: var(--r);
}
.odds-meter {
  position: relative; height: 22px; margin: var(--s2) 0 var(--s3);
  border: 2px solid var(--line); border-radius: var(--r); background: rgba(6, 9, 17, 0.7); overflow: hidden;
}
.odds-meter i {
  display: block; height: 100%;
  background: linear-gradient(90deg, rgba(226, 86, 76, 0.55), rgba(246, 204, 76, 0.75));
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.35);
}
.odds-meter b {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--font-display); font-size: var(--d-2); color: var(--text);
  text-shadow: 0 1px 0 #060911;
}
.odds-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  font-size: var(--t-1); color: var(--text-dim); padding: 4px var(--s2);
  border-bottom: 1px dashed rgba(92, 107, 134, 0.25);
}
.odds-row b { font-variant-numeric: tabular-nums; }
.job-card .pixel-btn.primary { margin-top: var(--s4); }

/* --- hiring: the CV queue, and the room where they ask the questions --- */
.hire-list { display: flex; flex-direction: column; gap: var(--s2); margin-bottom: var(--s4); }
.hire-card {
  padding: var(--s3); border: 2px solid var(--line); border-radius: var(--r);
  background: linear-gradient(180deg, rgba(30, 34, 60, 0.62), rgba(10, 12, 24, 0.5));
}
.hire-card.urgent { border-color: rgba(255, 209, 102, 0.5); }
.hire-card.stage-offer { border-color: rgba(74, 222, 128, 0.55); box-shadow: inset 0 0 22px rgba(74, 222, 128, 0.07); }
.hc-top { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; font-size: var(--t-1); color: var(--text-dim); }
.hc-job { font-size: var(--t-3); font-weight: 700; color: var(--gold); }
.hc-stage { padding: 2px 7px; border: 1px solid var(--line); border-radius: 999px; }
.hc-due { margin-left: auto; color: #ffe6a8; font-variant-numeric: tabular-nums; }
.hc-line { margin: var(--s2) 0; font-size: var(--t-2); line-height: 1.55; color: var(--text); }
.hc-meta { display: flex; gap: var(--s3); flex-wrap: wrap; font-size: var(--t-1); color: var(--text-dim); margin-bottom: var(--s2); }
.hc-meta .up { color: var(--success); }
.hc-meta .down { color: var(--danger); }
.hc-both { display: flex; gap: var(--s2); }
.hc-both .pixel-btn { flex: 1; }
.hire-dead {
  display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap;
  font-size: var(--t-1); color: var(--text-dim); padding: 5px var(--s2);
  border-bottom: 1px dashed rgba(92, 107, 134, 0.22);
}
.hire-dead span { color: var(--text-2); }
.hire-dead i { font-style: normal; flex: 1; min-width: 150px; }
.iv-room { font-size: var(--t-2); color: var(--text-dim); line-height: 1.5; }
.iv-room.boss { color: var(--text-2); margin-top: 4px; }
/* the trip card: one line of prose, then only what it actually cost */
.iv-room.trip { font-size: var(--t-3); color: var(--text-2); line-height: 1.65; }
.iv-note { font-size: var(--t-1); color: var(--text-dim); line-height: 1.5; margin-top: 3px; }
.iv-note:first-of-type { margin-top: var(--s3); }
.iv-dots { display: flex; gap: 5px; margin: var(--s3) 0 var(--s2); }
.iv-dots i { width: 22px; height: 4px; background: rgba(92, 107, 134, 0.35); border-radius: 2px; }
.iv-dots i.on { background: var(--success); }
.iv-dots i.now { background: var(--gold); }
.iv-q { font-size: var(--t-4); line-height: 1.5; font-weight: 700; margin-bottom: var(--s3); }
.iv-opts { display: flex; flex-direction: column; gap: var(--s2); }
.iv-both { display: flex; gap: var(--s2); align-items: center; }
.iv-both .pixel-btn.primary { flex: 1; }
.iv-early { margin-top: var(--s2); text-align: right; }
.iv-opt {
  display: flex; align-items: flex-start; gap: var(--s2); text-align: left; width: 100%;
  padding: 10px var(--s3); border: 2px solid var(--line); border-radius: var(--r);
  background: rgba(12, 16, 30, 0.55); color: var(--text); font-size: var(--t-2); line-height: 1.55; cursor: pointer;
}
.iv-opt:hover:not(:disabled) { border-color: rgba(255, 209, 102, 0.55); }
.iv-opt.picked { border-color: var(--gold); background: rgba(246, 204, 76, 0.1); }
.iv-opt.dim { opacity: 0.38; }
.iv-mark { flex: 0 0 auto; font-weight: 700; color: var(--gold); }
.iv-react {
  margin: var(--s3) 0; padding: 9px var(--s3); font-size: var(--t-2); line-height: 1.6;
  border-left: 3px solid var(--gold); background: rgba(246, 204, 76, 0.07); border-radius: var(--r);
}
.iv-verdict { font-size: var(--t-3); line-height: 1.6; margin-bottom: var(--s3); }
.iv-leave { font-size: var(--t-2); line-height: 1.55; color: var(--text-dim); margin-bottom: var(--s2); }
.iv-result.good .iv-verdict { color: var(--success); }
.iv-result.bad .iv-verdict { color: #ffb4a8; }
.iv-rows { margin-bottom: var(--s3); }
.chip-warn.hiring {
  border-color: rgba(255, 209, 102, 0.5); color: #ffe6a8; background: rgba(255, 209, 102, 0.12); cursor: pointer;
}
/* arrears and hunger both lead somewhere: the ledger, and the shop counter */
.chip-warn.arrears { border-color: rgba(255, 176, 92, 0.6); color: #ffd9ae; background: rgba(255, 176, 92, 0.15); cursor: pointer; }
.chip-warn.hungry { border-color: rgba(214, 158, 88, 0.55); color: #f0cf9f; background: rgba(214, 158, 88, 0.14); cursor: pointer; }
.wb-news.tone-good span { color: var(--success); }
.wb-news.tone-bad span { color: #ffb4a8; }
.wb-news.tone-special span { color: var(--gold); }
.epitaph p { text-align: center; font-size: var(--t-5); font-weight: 700; color: var(--gold); margin-bottom: var(--s4); }

/* --- city prosperity: the town's economy, shown as a number you can act on --- */
.prosper {
  padding: var(--s3); margin-bottom: var(--s4);
  border: 2px solid var(--line); border-radius: var(--r);
  background: linear-gradient(180deg, rgba(36, 40, 72, 0.6), rgba(10, 10, 26, 0.5));
}
.prosper.tone-great { border-color: rgba(74, 222, 128, 0.55); box-shadow: inset 0 0 24px rgba(74, 222, 128, 0.08); }
.prosper.tone-good { border-color: rgba(74, 222, 128, 0.35); }
.prosper.tone-ok { border-color: rgba(74, 158, 255, 0.3); }
.prosper.tone-warn { border-color: rgba(255, 209, 102, 0.35); }
.prosper.tone-bad { border-color: rgba(255, 107, 107, 0.45); box-shadow: inset 0 0 24px rgba(255, 107, 107, 0.07); }
.pr-top { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.pr-top .pr-icon { font-size: 16px; }
.pr-top b { font-size: var(--t-3); font-weight: 800; }
.tone-great .pr-top b, .tone-good .pr-top b { color: var(--success); }
.tone-warn .pr-top b { color: var(--gold); }
.tone-bad .pr-top b { color: var(--danger); }
.pr-idx {
  font-family: var(--font-display); font-size: var(--d-2); color: var(--text);
  padding: 4px 7px; border: 1px solid var(--line); border-radius: var(--r); background: rgba(6, 9, 17, 0.6);
}
.pr-tr { margin-left: auto; font-size: var(--t-1); font-variant-numeric: tabular-nums; }
.pr-tr i { font-style: normal; color: var(--text-dim); margin-left: 2px; }
.pr-meter {
  position: relative; height: 14px; margin: var(--s2) 0;
  border: 2px solid var(--line); border-radius: var(--r); background: rgba(6, 9, 17, 0.7); overflow: hidden;
}
.pr-meter i {
  display: block; height: 100%;
  background: linear-gradient(90deg, rgba(226, 86, 76, 0.6), rgba(246, 204, 76, 0.7) 45%, rgba(74, 222, 128, 0.75));
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.35);
}
.pr-head { font-size: var(--t-2); color: var(--text-2); line-height: 1.55; margin-bottom: var(--s3); }
.pr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.pr-grid > div {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  padding: 6px var(--s2); border: 1px solid var(--line-soft); border-radius: var(--r);
  background: rgba(255, 255, 255, 0.03); font-size: var(--t-1); color: var(--text-dim);
}
.pr-grid b { font-family: var(--font-display); font-size: var(--d-2); color: var(--text); }
.pr-drivers { margin-bottom: var(--s2); }
.drv-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  font-size: var(--t-2); color: var(--text-2); padding: 4px var(--s2);
  border-bottom: 1px dashed rgba(92, 107, 134, 0.25);
}
.drv-row b { font-variant-numeric: tabular-nums; }
.pr-ind { display: flex; flex-wrap: wrap; gap: var(--s2); }
.ind-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--t-1); color: var(--text-dim); padding: 4px 8px;
  border: 1px solid var(--line-soft); border-radius: var(--r); background: rgba(255, 255, 255, 0.02);
}
.ind-chip.pillar { color: var(--gold); border-color: rgba(255, 209, 102, 0.4); background: rgba(255, 209, 102, 0.07); }
.ind-chip .ico { font-size: 9px; }
.ind-chip b { color: var(--text-dim); font-size: var(--t-1); font-weight: 700; }
.ind-chip.pillar b { color: var(--gold); }
.ind-chip.up { border-color: rgba(74, 222, 128, 0.45); }
.ind-chip.up b { color: var(--success); }
.ind-chip.down { border-color: rgba(255, 107, 107, 0.45); }
.ind-chip.down b { color: var(--danger); }
.pr-grid b.up { color: var(--success); }
.pr-grid b.down { color: var(--danger); }

/* --- career board header: what today's town is doing --- */
.career-board {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: 7px var(--s3); margin-bottom: var(--s3);
  border: 1px solid var(--line-soft); border-radius: var(--r); background: rgba(255, 255, 255, 0.03);
  font-size: var(--t-1);
}
.cb-city { font-size: var(--t-2); font-weight: 700; color: var(--text); }
.cb-stage { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px; border-radius: var(--r); border: 1px solid var(--line); }
.cb-stage.tone-great, .cb-stage.tone-good { color: var(--success); border-color: rgba(74, 222, 128, 0.45); }
.cb-stage.tone-ok { color: var(--accent-2); border-color: rgba(68, 221, 255, 0.4); }
.cb-stage.tone-warn { color: var(--gold); border-color: rgba(255, 209, 102, 0.45); }
.cb-stage.tone-bad { color: var(--danger); border-color: rgba(255, 107, 107, 0.5); }
.cb-open b { color: var(--gold); font-family: var(--font-display); font-size: var(--d-2); }
.cb-risk { margin-left: auto; color: var(--danger); }
.cb-lang { color: var(--text-dim); }
.cb-lang.cb-risk { color: var(--danger); }
/* the career calendar sits between the city board and the live applications */
.career-status {
  display: flex; flex-direction: column; gap: 3px;
  margin-bottom: var(--s3); padding: 6px var(--s3);
  border: 1px solid var(--line-soft); border-left: 2px solid var(--line);
  border-radius: var(--r); background: rgba(255, 255, 255, 0.02);
}
.cs-row { display: flex; align-items: flex-start; gap: 6px; font-size: var(--t-2); line-height: 1.5; color: var(--text-2); }
.cs-row .ico { font-size: 15px; margin-top: 1px; opacity: 0.9; flex: 0 0 auto; }
.cs-warn { color: var(--gold); }
.cs-good { color: var(--success); }
.cs-bad { color: var(--danger); }
.cs-dim { color: var(--text-dim); }
.cs-plain { color: var(--text-dim); }
.sector-tag {
  display: inline-block; margin-left: 6px; padding: 1px 5px;
  font-size: var(--t-1); color: var(--accent-2); border: 1px solid rgba(68, 221, 255, 0.3);
  border-radius: var(--r); background: rgba(68, 221, 255, 0.07); vertical-align: 1px;
}
/* a seat whose contract came from another town */
.sector-tag.deal {
  color: var(--gold); border-color: rgba(255, 209, 102, 0.42);
  background: rgba(255, 209, 102, 0.08);
}

/* --- course catalogue --- */
.study-city {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: 7px var(--s3); margin: var(--s2) 0 var(--s3);
  border: 1px solid var(--line-soft); border-radius: var(--r); background: rgba(255, 255, 255, 0.03);
}
.study-city b { font-family: var(--font-display); font-size: var(--d-2); color: var(--gold); }
.study-city .pane-hint { margin: 0; flex: 1 1 220px; }
.course-row { padding: var(--s2) 0 var(--s3); border-bottom: 1px solid var(--line-soft); }
.cr-head { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s2); }
.cr-head b { font-size: var(--t-3); font-weight: 700; color: var(--text); }
.cr-lv { font-family: var(--font-display); font-size: var(--d-2); color: var(--accent-2); }
.cr-hub {
  margin-left: auto; font-size: var(--t-1); color: var(--success);
  padding: 3px 6px; border: 1px solid rgba(74, 222, 128, 0.45); border-radius: var(--r);
}
.cr-far { margin-left: auto; font-size: var(--t-1); color: var(--text-dim); }
.cr-tiers { display: flex; gap: var(--s2); flex-wrap: wrap; }
.pixel-btn.tiny.ghost {
  color: var(--text-dim); background: rgba(255, 255, 255, 0.02); border-color: var(--line-soft);
  cursor: not-allowed;
}
.pixel-btn.tiny.ghost .ico { font-size: 9px; opacity: 0.7; }

/* --- the return card: what the city did while you were not in it --- */
.wb { display: flex; flex-direction: column; gap: var(--s3); }
.wb-head {
  font-size: var(--t-2); color: var(--text-2); line-height: 1.65;
  padding: var(--s3); border: 1px solid var(--line-soft); border-radius: var(--r);
  background: linear-gradient(180deg, rgba(68, 221, 255, 0.06), rgba(0, 0, 0, 0));
}
.wb-city {
  display: flex; align-items: center; gap: var(--s2); font-size: var(--t-2);
  padding: 6px var(--s3); border: 1px solid var(--line); border-radius: var(--r);
}
.wb-city b { margin-left: auto; font-family: var(--font-display); font-size: var(--d-2); }
.wb-city.tone-great, .wb-city.tone-good { color: var(--success); border-color: rgba(74, 222, 128, 0.4); }
.wb-city.tone-ok { color: var(--accent-2); border-color: rgba(68, 221, 255, 0.35); }
.wb-city.tone-warn { color: var(--gold); border-color: rgba(255, 209, 102, 0.4); }
.wb-city.tone-bad { color: #ffc9c9; border-color: rgba(255, 107, 107, 0.45); }
.wb-rows { display: flex; flex-direction: column; }
.wb-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  padding: 5px 0; border-bottom: 1px dashed var(--line-soft); font-size: var(--t-2); color: var(--text-2);
}
.wb-row b { font-family: var(--font-display); font-size: var(--d-2); color: var(--text); }
.wb-row b.up, .wb-net b.up { color: var(--success); }
.wb-row b.down, .wb-net b.down { color: var(--danger); }
.wb-net {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 6px; border-top: 2px solid var(--line); font-size: var(--t-3); font-weight: 700; color: var(--text);
}
.wb-net b { font-family: var(--font-display); font-size: var(--d-3); }
.wb-note { font-size: var(--t-1); color: var(--text-dim); padding-left: 2px; }
.wb-note.bad { color: #ffc9c9; }
.wb-news {
  display: flex; gap: var(--s2); align-items: flex-start; font-size: var(--t-2); color: var(--text-2);
  padding: 4px 0;
}
.wb-news .ico { flex: 0 0 auto; margin-top: 1px; opacity: 0.8; }
.wb-todos { display: flex; flex-direction: column; gap: var(--s2); }
.wb-todo {
  display: flex; align-items: center; gap: var(--s2); width: 100%; text-align: left;
  padding: 8px var(--s3); cursor: pointer; font: inherit; color: var(--text);
  background: rgba(255, 255, 255, 0.04); border: 1px solid var(--line); border-radius: var(--r);
}
.wb-todo:hover { background: rgba(255, 255, 255, 0.08); }
.wb-todo span { flex: 1; font-size: var(--t-2); }
.wb-todo i { font-size: var(--t-1); font-style: normal; letter-spacing: 1px; color: var(--gold); }
.wb-todo.tone-good { border-color: rgba(74, 222, 128, 0.38); }
.wb-todo.tone-bad { border-color: rgba(255, 107, 107, 0.42); }
/* --- the year jump: the verdict is the headline, everything else is receipts --- */
.wb-verdict {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2);
  padding: var(--s3); border: 1px solid var(--line); border-radius: var(--r);
  background: rgba(255, 255, 255, 0.05);
}
.wb-verdict span { font-size: var(--t-3); font-weight: 700; line-height: 1.6; }
.wb-verdict i { flex: 1 100%; font-style: normal; font-size: var(--t-1); color: var(--text-dim); }
.wb-verdict.tone-good { border-color: rgba(74, 222, 128, 0.45); }
.wb-verdict.tone-good span { color: var(--success); }
.wb-verdict.tone-bad { border-color: rgba(255, 107, 107, 0.5); }
.wb-verdict.tone-bad span { color: #ffb4a8; }
.wb-verdict.tone-normal span { color: var(--accent-2); }
.wb-day { font-family: var(--font-display); font-size: var(--d-1); letter-spacing: 1px; color: var(--text-dim); margin-right: 5px; }
.wb-body { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); font-size: var(--t-2); color: var(--text-2); }
.wb-body .ico { margin-right: 4px; opacity: 0.8; }
.jump-actions { display: flex; flex-direction: column; gap: var(--s2); }

/* --- travel board: every town's cycle on one screen --- */
.tc-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 3px; }
.tc-tr { font-size: var(--t-1); font-variant-numeric: tabular-nums; }
.tc-open { font-size: var(--t-1); color: var(--text-dim); }
.list-item .sub.dim { opacity: 0.7; }

/* ============================ NOTIFICATIONS ============================ */
.notification {
  position: fixed;
  top: calc(var(--hud-h) + var(--s3));
  left: 50%;
  transform: translateX(-50%);
  max-width: min(560px, 90vw);
  padding: 10px var(--s4);
  font-size: var(--t-2);
  line-height: 1.55;
  border: 2px solid var(--accent);
  border-radius: var(--r);
  background: rgba(10, 14, 34, 0.95);
  color: var(--accent);
  z-index: 2000;
  animation: notifyIn 0.25s ease-out, notifyOut 0.3s ease-in 2.6s forwards;
  pointer-events: none;
  box-shadow: var(--shadow);
}
.notification.good { border-color: var(--success); color: var(--success); background: rgba(8, 30, 20, 0.95); }
.notification.bad { border-color: var(--danger); color: #ffb3b3; background: rgba(34, 8, 12, 0.95); }
@keyframes notifyIn { from { opacity: 0; transform: translateX(-50%) translateY(-14px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
@keyframes notifyOut { from { opacity: 1; } to { opacity: 0; transform: translateX(-50%) translateY(-14px); } }
.notification.out { animation: notifyOut 0.4s ease-in forwards; }

/* ============================ SCROLLBAR ============================ */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #333a63; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #4a5480; }

/* ============================ RESPONSIVE ============================ */
@media (max-width: 1280px) {
  :root { --dock-w: 380px; }
  .hud-group.vitals { min-width: 210px; }
  .ticker { width: 46%; }
}

@media (max-width: 1020px) {
  :root { --hud-h: auto; }
  .hud { flex-wrap: wrap; padding: var(--s2) var(--s3); gap: var(--s2) var(--s3); }
  .hud-group { height: auto; padding: 0 var(--s2) 0 0; border-right: 1px solid var(--line-soft); }
  .hud-group.vitals { min-width: 180px; }
  .hud-group.wealth .wealth-sub { display: none; }
}

/* laptop heights: the module column must show every panel without scrolling.
   Thirteen of them is 667px of buttons and a 900px window gives the column 579, so the
   account row goes two-up and the group rules go with it — that is 117px back, and 资讯,
   持仓 and 彩蛋 stop living below a fold nobody thinks to scroll on. */
@media (max-height: 940px) {
  .rail-btn { height: 44px; gap: 2px; }
  .rail-icon { font-size: 17px; }
  .rail-sep { display: none; }
  .rail { padding: var(--s1) 0; }
  .rail-foot { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; padding-bottom: 0; }
  .rail-foot .rail-btn { width: auto; }
}

@media (max-height: 760px) {
  .rail-btn { height: 42px; gap: 1px; }
  .rail-icon { font-size: 15px; }
  .rail-label { line-height: 1.1; }
}

@media (max-width: 820px) {
  :root { --rail-w: 58px; --bar-h: 66px; --dock-w: 100%; }
  .rail-btn { height: 46px; }
  .rail-icon { font-size: 16px; }
  .rail-icon .ico { font-size: 19px; }
  .hot-icon .ico { font-size: 19px; }
  .rail-label { max-width: calc(var(--rail-w) - var(--s2)); }
  /* the top readout must not eat the world: one tight block, small type */
  .hud { gap: 0 var(--s2); padding: var(--s1) var(--s2); }
  .hud-group { border-right: 0; padding: 0 var(--s2) 0 0; gap: 1px; }
  .hud-group.vitals { min-width: 146px; gap: 2px; padding: 0; }
  .hud-group.wealth { min-width: 0; }
  .stat-bar { height: 6px; min-width: 70px; }
  .bar-name { width: 28px; font-size: var(--t-1); }
  .bar-val { width: 26px; font-size: var(--d-1); }
  .id-name { font-size: var(--t-1); max-width: 92px; }
  .hud-value.big { font-size: var(--t-3); }
  .hud-sub { font-size: var(--t-1); max-width: 118px; }
  .hud-group.time { min-width: 0; }
  .hud-value.clock { font-size: var(--d-2); }
  .dock { left: var(--s2); right: var(--s2); top: var(--s2); width: auto; max-width: none; }
  .ticker { display: none; }
  .hot-text { display: none; }
  .hot-btn { padding: 0 var(--s2); }
  .sleep-btn { padding: 0 var(--s2); }
  .sleep-text { min-width: 120px; }
  .sleep-btn kbd { display: none; }
}

@media (max-width: 600px) {
  /* keep identity, the clock, vitals and cash on the readout; the city and job
     live in their own modules */
  .hud-group.place, .hud-group.market .hud-label { display: none; }
  .status-chips { display: none; }
}

@media (max-width: 470px) {
  .hud-group.market { display: none; }
  .so-bar { width: 180px; }
}

/* ----- settings panel ----- */
.set-group { margin-bottom: var(--s3); }
.set-group h4 {
  font-size: var(--t-3);
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--gold);
  margin: 0 0 var(--s2);
}
.set-toggle {
  font-size: var(--t-1);
  font-weight: 600;
  letter-spacing: 1px;
  padding: 0 var(--s2);
  border: 1px solid var(--line, #3a4560);
  border-radius: var(--r-2);
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.04);
  min-width: 44px;
  min-height: var(--ctl-sm);
  text-align: center;
  flex-shrink: 0;
}
.set-toggle.on {
  color: var(--success);
  border-color: var(--success);
  background: rgba(60, 200, 120, 0.1);
}
.set-group input[type="range"] {
  width: 130px;
  accent-color: var(--gold);
  cursor: pointer;
}
#rail-settings .rail-icon { color: var(--text-dim); }
#rail-settings:hover .rail-icon, #rail-settings.active .rail-icon { color: var(--gold); }

/* the quality row is four choices, not a switch: the label has to say which one is live */
.quality-row { flex-wrap: wrap; }
.quality-opts { display: flex; gap: var(--s1); flex-shrink: 0; }
.q-opt {
  font-size: var(--t-1);
  font-weight: 600;
  letter-spacing: 0.5px;
  padding: 0 var(--s2);
  min-height: var(--ctl-sm);
  color: var(--text-dim);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line, #3a4560);
  border-radius: var(--r-2);
  cursor: pointer;
}
.q-opt.on { color: var(--gold); border-color: var(--gold); background: rgba(255, 209, 102, 0.12); }
.quality-note { width: 100%; margin-top: var(--s1); color: var(--text-dim); font-size: var(--t-1); line-height: 1.55; }

/* ============================ TOUCH / MOBILE H5 ============================
   A phone is a screen with no keys and a thumb that is not a cursor. The canvas
   takes every gesture, the panels keep only vertical scrolling, and the chrome
   steps back from the notch, the rounded corners and the home bar. */
html { -webkit-text-size-adjust: 100%; }

body {
  overscroll-behavior: none;
  -webkit-tap-highlight-color: transparent;
}

/* the world swallows touches; the panels still have to scroll under a finger */
#gameCanvas { touch-action: none; }
.dock-body, .modal-body, .log-list { touch-action: pan-y; -webkit-overflow-scrolling: touch; }
button, .rail-btn, .hot-btn, .list-item { touch-action: manipulation; }
input, textarea, select { user-select: text; -webkit-user-select: text; }

/* the shell hugs the safe area instead of hiding under the status bar */
.hud { padding: var(--s1) calc(var(--s3) + env(safe-area-inset-right)) calc(var(--s1) + env(safe-area-inset-top)) calc(var(--s3) + env(safe-area-inset-left)); }
.rail { padding-left: env(safe-area-inset-left); }
.dock { top: calc(var(--s3) + env(safe-area-inset-top)); right: calc(var(--s3) + env(safe-area-inset-right)); }
.bar {
  left: calc(var(--s3) + env(safe-area-inset-left));
  right: calc(var(--s3) + env(safe-area-inset-right));
  bottom: calc(var(--s3) + env(safe-area-inset-bottom));
}
#modal .modal-content, #deathModal .modal-content {
  margin: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}

/* a finger is 44px wide, and every keyboard hint is a lie on a touch device.
   .is-touch is set once at boot from the pointer media query: one flag decides the
   chrome sizes here and the canvas hint text in the engine, so the two can never
   disagree about which device the player is holding */
html.is-touch .rail-btn { min-height: 46px; }
html.is-touch .dock-close { width: 40px; height: 40px; font-size: var(--t-3); }
html.is-touch .hot-btn { min-height: 44px; }
html.is-touch .q-opt { padding: 7px 10px; }
html.is-touch .set-toggle { padding: 6px 10px; }
html.is-touch .pixel-input { font-size: var(--t-4); }
html.is-touch kbd, html.is-touch .hot-key, html.is-touch .sleep-btn kbd { display: none; }
/* hover was the only way some of this surfaced: tap has to say the same thing */
html.is-touch .list-item:hover { transform: none; }
html.is-touch .list-item:active { transform: translateX(2px); background: rgba(74, 158, 255, 0.1); }
/* an 8px chrome bar is a desktop widget, and on a phone it costs real height: the key
   strip spent 8px of its band on a track nobody can tap, and the rail spent 8px of its
   60px column. The key row and the chip row can lose theirs because both leave a tile
   half-cut at the edge — the cut is the affordance, and a finger drags.
   The rail is the exception and keeps a thumb, slimmed: at 667px tall it shows 7 of 10
   modules and the 8th ends flush, so nothing on screen says "there is more" but the bar. */
html.is-touch .hotbar::-webkit-scrollbar,
html.is-touch .status-chips::-webkit-scrollbar { display: none; }
html.is-touch .hotbar, html.is-touch .status-chips { scrollbar-width: none; }
html.is-touch .rail-scroll::-webkit-scrollbar { width: 4px; }
html.is-touch .rail-scroll { scrollbar-width: thin; }

/* a phone held in portrait has no room for a second readout column.
   Its --hud-h/--bar-h used to be set here too; the PHONE SHELL block below repeats
   this exact media query and overrode both, so the numbers here were never painted. */
@media (max-width: 720px) and (orientation: portrait) {
  .hud-group.vitals { min-width: 132px; }
  .so-bar { width: 150px; }
  .modal-content { max-width: none; width: 100%; max-height: calc(100dvh - 12px); }
  .modal-body { max-height: calc(100dvh - 108px); }
}

/* landscape phones: the world is only a strip, so the chrome shrinks to match */
@media (max-height: 460px) and (orientation: landscape) {
  :root { --hud-h: 44px; --bar-h: 54px; --rail-w: 46px; }
  .hud-group.wealth .hud-sub, .id-name { display: none; }
  .stat-bar { height: 5px; min-width: 56px; }
  .rail-label { max-width: calc(var(--rail-w) - var(--s1)); }
  .dock { top: 4px; bottom: calc(var(--bar-h) + 6px); }
  .modal-content { max-height: calc(100dvh - 8px); }
}

/* ============================ PHONE SHELL (H5) ============================
   The blocks above fit the chrome to a small *window*. These fit it to a small
   *body*: a notch, a home bar, and a camera that pins the player's feet to 62% of
   the world height, so no panel may be laid on top of them. Scoped to a portrait
   phone, a landscape phone, or html.is-touch — none of which a desktop viewport
   can reach, so the desktop paint is untouched. */

@media (max-width: 720px) and (orientation: portrait) {
  /* --hud-h goes back to auto, the way the 1020px block already chose for a narrow
     window: a fixed 66px minus the status-bar inset leaves a 15px sliver for a
     readout that wraps to two rows, and .hud clips what it cannot show. Auto lets
     the strip be as tall as its own groups, and overflow:hidden still keeps it in
     its band. --bar-h is the height of a two-row action bar — see .bar below — and
     the ticker is anchored off this number, so it has to be the real height. */
  :root { --hud-h: auto; --bar-h: 142px; --rail-w: 60px; }
  .hud {
    padding: calc(var(--s1) + env(safe-area-inset-top)) calc(var(--s3) + env(safe-area-inset-right))
             var(--s1) calc(var(--s3) + env(safe-area-inset-left));
    gap: var(--s1) var(--s2);
  }
  .hud-sub { max-width: min(184px, 44vw); }
  .id-name { max-width: min(150px, 38vw); }

  /* 13 anonymous pixel sprites is not a menu. The 820px block dropped the rail
     labels for want of width; the column pays for them instead and the label takes
     an ellipsis, so a long English word reads "Awards…" rather than overflowing. */
  .rail-label {
    display: block;
    max-width: calc(var(--rail-w) - var(--s2));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* the panel stops at the camera's anchor line: it used to run the full height of
     the world, avatar and all. What the panel cannot show, .dock-body scrolls. */
  .dock {
    left: var(--s2);
    right: var(--s2);
    top: calc(var(--s2) + env(safe-area-inset-top));
    bottom: 38%;
  }
  .dock-body { padding: var(--s2); }

  /* 13 modules and 4 account buttons will not stand in one 60px column on a 667px phone:
     measured, the list had 342px for 641px of buttons and six modules — 资讯, 持仓, 彩蛋
     among them — sat below the fold. The account row is the cheap half of that height, so
     the max-height:940px block above puts it two-up and drops the group rules. That leaves
     the module list one or two buttons longer than the column on the smallest phones, which
     is what the slim thumb and the drag are for; on an 844px phone all 13 stand up. */

  /* the bar is two rows on a phone, and the reason is the keys. Measured on a 390px body:
     the night button and its plan take ~230px of the ~358 the row has, ⋯ takes 48, and
     .hotbar — flex:1 with a min-width of 0 — was left with nothing at all. The five daily
     actions did not shrink on a phone, they vanished, and the only door left was a small
     ⋯. So the keys get their own row: the full width, and the bottom edge where a thumb
     already is. The night, the warnings and ⋯ share the row above them. */
  .bar { flex-wrap: wrap; align-content: flex-end; gap: var(--s2); padding: var(--s2); }
  .hotbar { order: 3; flex: 0 0 100%; max-width: 100%; gap: var(--s1); }
  /* the 820px block turned a key into an icon with no name — five anonymous sprites is
     not a menu, which is exactly the failure the rail had and why the rail paid for its
     labels. Now that the keys own a row and the whole width, they stand up: icon over
     name over price. A strip that cannot show them all scrolls, and the last one peeks. */
  .hot-btn {
    flex: 0 0 auto; min-width: 52px; height: auto; min-height: 52px;
    flex-direction: column; justify-content: center; gap: 0; padding: 4px var(--s1);
  }
  .hot-text { display: flex; align-items: center; gap: 0; }
  .hot-label { font-size: var(--t-1); line-height: 1.15; }
  .hot-cost { font-size: var(--t-1); line-height: 1.15; }
  .more-btn { order: 1; }
  .bar-right {
    order: 2; flex: 1 1 auto; min-width: 0;
    flex-direction: row; align-items: center; justify-content: flex-end; gap: var(--s2);
  }

  /* the alert chips are the only door to the risk sheet, and they carry the
     hospital, the interview queue and the arrears ledger: hiding them behind
     display:none at 600px would lock a phone out of features the desktop has.
     They get a scrollable strip here — the same panels remain reachable from the
     rail and the Daily list, so the strip is a reminder rather than the only way in. */
  .status-chips { display: flex; gap: var(--s1); flex: 1 1 auto; min-width: 0; overflow-x: auto; }
  /* a chip that shrinks wraps one glyph per line, so three of them became three columns
     taller than the bar and stood on top of the ticker. The strip scrolls sideways; a chip
     never does. */
  .status-chips .chip-warn { flex: 0 0 auto; white-space: nowrap; }
  /* the night button takes whatever row A has left, because its plan line is the one
     string here that cannot be cut: "09:52→17:22 7.5h 62%" needs ~200px, and pinned to
     its own content width it got 156 and dropped the quality onto a third line alone.
     With no warnings up, that is 230px and the line holds; with warnings up it wraps
     inside the 48px box rather than pushing the row apart. */
  .sleep-btn { height: 48px; padding: 0 var(--s3); flex: 1 1 auto; min-width: 0; }
  .sleep-text { width: auto; min-width: 0; max-width: 100%; }
  .sleep-text em { white-space: normal; max-width: 100%; line-height: 1.2; }

  /* the log is not decoration: the newest line above the bar, under the avatar's feet,
     inside a pointer-events:none band so no tap is stolen from the world. The lines
     pack against the bottom edge, so a capped height drops the oldest, never the
     newest — which is the line the player is still reading.
     On a phone the band is 314px wide and a sentence is not 314px long: nowrap used to
     cut 「…或点地面走动，」 and the payoff of the line was gone. The newest row gets two
     clamped lines; whatever is above it is history and may be clipped. */
  .ticker {
    display: flex;
    left: var(--s2);
    right: var(--s2);
    width: auto;
    bottom: calc(var(--bar-h) + var(--s3) + env(safe-area-inset-bottom));
    max-height: 88px;
    justify-content: flex-end;
    overflow: hidden;
  }
  .ticker-row {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    white-space: normal;
  }

  /* the sheet inside the safe area: with the inset carried as a margin on a centred
     flex item, a tall panel outgrew the screen and lost its close mark off the top */
  .modal {
    padding: calc(var(--s2) + env(safe-area-inset-top)) calc(var(--s2) + env(safe-area-inset-right))
             calc(var(--s2) + env(safe-area-inset-bottom)) calc(var(--s2) + env(safe-area-inset-left));
  }
  .modal-content { max-height: 100%; }
  #modal .modal-content, #deathModal .modal-content { margin: 0; }
}

@media (max-width: 480px) and (orientation: portrait) {
  /* the day card is a title, not a second panel: it may not be wider than the hand */
  .day-card .dc-frame {
    min-width: 0;
    width: 76vw;
    padding: var(--s4) var(--s4) var(--s3);
  }
  .dc-day { font-size: var(--t-5); letter-spacing: 1px; margin: var(--s2) 0 var(--s1); }
  .dc-meta { flex-wrap: wrap; gap: var(--s1) var(--s2); }
  .login-box { padding: var(--s5) var(--s4); }
}

@media (max-height: 460px) and (orientation: landscape) {
  /* same reason as portrait: a 44px band cannot hold a HUD that wraps under a wide
     notch, and the rail's safe-area padding used to be taken out of its own 46px */
  :root { --hud-h: auto; --rail-w: calc(46px + env(safe-area-inset-left)); }
  .hud { gap: 0 var(--s1); }
  /* the name is identity, not spare detail: small and ellipsised instead of gone */
  .id-name { display: block; font-size: var(--t-1); max-width: 104px; }
  .hud-group.place .hud-sub { max-width: 96px; }

  /* keep the strip on the side it always used: the avatar is centred, so a panel
     that spans the world would sit on it. 46% of the width starts past the middle. */
  .dock {
    left: auto;
    width: min(46%, 340px);
    min-width: 210px;
    top: calc(var(--s2) + env(safe-area-inset-top));
    bottom: calc(var(--bar-h) + var(--s2) + env(safe-area-inset-bottom));
  }
  .status-chips { display: flex; gap: var(--s1); max-width: min(34vw, 240px); overflow-x: auto; }
  .sleep-text { width: auto; min-width: 0; max-width: 40vw; }
  .sleep-text em { white-space: normal; line-height: 1.3; }
  .ticker {
    display: flex;
    width: min(46%, 320px);
    bottom: calc(var(--bar-h) + var(--s2));
    max-height: 51px;
    justify-content: flex-end;
    overflow: hidden;
  }
  .modal {
    padding: var(--s2) calc(var(--s2) + env(safe-area-inset-right))
             calc(var(--s2) + env(safe-area-inset-bottom)) calc(var(--s2) + env(safe-area-inset-left));
  }
  .modal-content { max-height: 100%; }
  #modal .modal-content, #deathModal .modal-content { margin: 0; }
}

/* thumbs, not cursors: sized from the flag the engine already reads, so a desktop
   window that happens to be narrow keeps its pointer-sized rows */
html.is-touch .more-btn { width: 48px; }
html.is-touch .modal-x { width: 40px; height: 40px; }
html.is-touch .action-btn { padding-top: 14px; padding-bottom: 14px; }
html.is-touch .list-item.row { min-height: 44px; }
html.is-touch .mini-row { padding: 11px var(--s2); }
html.is-touch .news-item { padding: var(--s3) 10px; }
html.is-touch .chip { padding: 8px 12px; }
html.is-touch .wb-todo { padding: 12px var(--s3); }
html.is-touch .iv-opt { padding: 14px var(--s3); }
html.is-touch .modal-content .trap-opt { padding: 14px var(--s3); }
html.is-touch .pixel-btn.tiny { padding: 9px 14px; min-height: 40px; }
html.is-touch .pixel-input { padding: 12px; }
/* the 16px that stops iOS zooming a focused field must not turn the quantity box
   into a brick: it keeps its own compact padding and grows only to a knuckle */
html.is-touch .pixel-input.tiny { padding: var(--s2) 6px; }
html.is-touch .login-form .pixel-btn { min-height: 46px; }

/* the toast: --hud-h is auto on any narrow body, which makes that centred top
   offset invalid at computed-value time and drops the notification at the foot of
   the document, off screen. On a finger device it lands above the action bar, where
   the thumb already is and nothing it says is hidden behind the HUD. */
html.is-touch .notification {
  top: auto;
  bottom: calc(var(--bar-h) + var(--s5) + env(safe-area-inset-bottom));
  max-width: min(92vw, 480px);
}

/* login: the phone keyboard takes the lower half of the screen, so the box must be
   able to scroll rather than sit pinned under it, and it must fit the safe area */
html.is-touch #loginScreen {
  align-items: flex-start;
  justify-content: flex-start;
  overflow-y: auto;
  padding: calc(var(--s3) + env(safe-area-inset-top)) var(--s3) calc(var(--s3) + env(safe-area-inset-bottom));
}
html.is-touch .login-box { margin: auto; width: min(100%, 440px); }

