/* ---------------- Top status bar ---------------- */
#status-bar {
  position: absolute;
  top: 0; left: 0; right: 0;
  padding: calc(env(safe-area-inset-top, 0px) + clamp(10px, calc(2.6 * var(--u)), 26px)) clamp(14px, calc(3.6 * var(--u)), 36px) clamp(10px, calc(2.6 * var(--u)), 26px);
  background: linear-gradient(#11160fe6, #11160f99 70%, #11160f00);
  pointer-events: none;
  z-index: 20;
}
.status-line {
  display: flex; align-items: center; gap: clamp(9px, calc(2.4 * var(--u)), 22px); margin-bottom: clamp(6px, calc(1.6 * var(--u)), 14px);
  font-size: clamp(16px, calc(4.6 * var(--u)), 38px); flex-wrap: wrap;
}
.s-name { font-weight: 700; font-size: clamp(20px, calc(6 * var(--u)), 50px); color: var(--accent); }
/* Boost pill is always shown and tappable (the status bar itself ignores pointer
   events, so re-enable them here). Dimmed when no boost is active ("empty"). */
#s-boost { pointer-events: auto; cursor: pointer; }
.pill.boost.empty { background: #1b2a1fcc; color: var(--muted); border-color: #314733; font-weight: 600; }
/* Top line as a 4-column grid so the boost pill sits dead-centre between the name
   (left) and the online count (right). Columns are pinned explicitly so hiding the
   boost doesn't shift the name/online out of place. Column 3 holds #event-badges (the
   generic timed-event badge row — event-badges.js) and is empty (zero-width) whenever
   no event is running; it flexes internally to however many badges are active at once,
   so this stays a 4-column grid no matter how many timed events exist. */
.top-line { display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto; align-items: center; flex-wrap: nowrap; }
.top-line .name-lv { grid-column: 1; justify-self: start; display: flex; align-items: center; gap: clamp(8px, calc(2 * var(--u)), 18px); min-width: 0; }
.name-lv .s-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.name-lv .pill { flex: none; }
/* Only wide screens have room to pin the boost dead-centre between name+Lv and the online
   badge; anywhere narrower it would overlap the Lv pill, so the default above keeps the
   boost right after the name cluster instead. */
@media (min-width: 768px) {
  .top-line { grid-template-columns: 1fr auto 1fr; }
}
/* Below that, compact the boost/Lv pills so name+Lv+boost+online fit one line;
   the name ellipsizes as the last resort (player names can be 20 chars). */
@media (max-width: 767px) {
  .top-line #s-boost { font-size: 14px; padding: 4px 10px; }
  .name-lv .pill { font-size: 13px; padding: 3px 9px; }
}
/* Narrow phones: the name itself comes down a notch too, and the row packs tighter. */
@media (max-width: 520px) {
  .name-lv .s-name { font-size: 16px; }
  .top-line { column-gap: 6px; }
  .name-lv { gap: 6px; }
  /* Keep the distance chip beside the HP bar (at full size the pair just overflows the row). */
  .hp-line .s-dist { font-size: 13px; }
}
.top-line #s-boost { grid-column: 2; justify-self: center; }
.top-line #event-badges { grid-column: 3; justify-self: end; }
.top-line .s-online { grid-column: 4; justify-self: end; margin-left: 0; }

/* Timed-event badge row: only in the DOM while at least one random-start event is running
   (event-badges.js adds/removes the container and its child pills), sitting immediately left
   of the online pill. Tapping any pill opens #event-modal, populated per-event by JS. */
#event-badges { display: flex; align-items: center; gap: 6px; }
.evt-badge {
  pointer-events: auto; cursor: pointer; -webkit-tap-highlight-color: transparent;
  line-height: 1; user-select: none;
  font-size: clamp(15px, calc(3.6 * var(--u)), 30px);
  filter: drop-shadow(0 0 6px #ffe9a655);
  animation: uni-bob 2.4s ease-in-out infinite;
}
.evt-badge:active { opacity: 0.7; }
@keyframes uni-bob { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(-2px) } }
@media (prefers-reduced-motion: reduce) { .evt-badge { animation: none } }
.s-online { margin-left: auto; color: var(--muted); display: flex; align-items: center; gap: 6px; }
#s-online-badge { pointer-events: auto; cursor: pointer; -webkit-tap-highlight-color: transparent; }
#s-online-badge:active { opacity: 0.7; }
.s-online .dot { width: clamp(11px, calc(2.4 * var(--u)), 20px); height: clamp(11px, calc(2.4 * var(--u)), 20px); border-radius: 50%; background: var(--accent-2); box-shadow: 0 0 6px var(--accent-2); }
.s-version { margin-left: 8px; color: var(--muted); opacity: 0.45; font-size: clamp(9px, calc(2 * var(--u)), 13px); letter-spacing: 0.3px; font-weight: 600; }
.pill {
  background: #1b2a1fcc; border: 1px solid #314733; border-radius: 999px;
  padding: clamp(6px, calc(1.6 * var(--u)), 14px) clamp(15px, calc(3.8 * var(--u)), 34px); font-size: clamp(16px, calc(4.6 * var(--u)), 38px);
}
/* HP bar sitting under the player name (replaces the old HP pill): the fill width is
   driven by ui.js setHpDisplay; low/healing keep the pill's colour language. */
.hp-bar {
  position: relative; width: clamp(150px, calc(38 * var(--u)), 300px);
  height: clamp(22px, calc(5.6 * var(--u)), 44px);
  flex: 1 1 auto; min-width: 90px; max-width: clamp(150px, calc(38 * var(--u)), 300px);
  background: #26150fd9; border: 1px solid #5a2c25; border-radius: 999px; overflow: hidden;
  transition: border-color .25s;
}
.hp-fill { position: absolute; inset: 0; width: 100%; background: linear-gradient(#5aa14e, #2f6b33); transition: width .35s ease; }
.hp-text {
  position: relative; display: flex; align-items: center; justify-content: center; height: 100%;
  font-size: clamp(13px, calc(3.6 * var(--u)), 28px); font-weight: 700; color: #f2efe6;
  text-shadow: 0 1px 2px rgba(0,0,0,.65); font-variant-numeric: tabular-nums;
}
/* Low-HP warning: HP at or below 30% — red fill, brighter, and a gentle pulse to prompt a heal/retreat. */
#hp-bar.low { border-color: #d24a4a; animation: hp-low-pulse 1.1s ease-in-out infinite; }
#hp-bar.low .hp-fill { background: linear-gradient(#d24a4a, #8a2424); }
/* Healing cue: resting and recovering (and not low) — a calm green glow. */
#hp-bar.healing { border-color: #4a8a4a; animation: hp-heal-pulse 1.6s ease-in-out infinite; }
@keyframes hp-heal-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(90,200,90,0); } 50% { box-shadow: 0 0 9px 1px rgba(90,200,90,.5); } }
@keyframes hp-low-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(210,74,74,.0); } 50% { box-shadow: 0 0 10px 2px rgba(210,74,74,.6); } }
/* Combat level-up: a brief gold flash on the Lv pill when the headline level rises (live play). */
.pill.cl-up { animation: cl-up-flash 1.5s ease-out 1; }
@keyframes cl-up-flash {
  0% { box-shadow: 0 0 0 0 rgba(255,210,60,0); color: #ffe89a; }
  18% { box-shadow: 0 0 14px 3px rgba(255,210,60,.8); color: #fff4c2; }
  100% { box-shadow: 0 0 0 0 rgba(255,210,60,0); }
}
/* Critical-HP (≤20%) red edge vignette — a subtle, pulsing "you're in danger" wash. Never
   blocks input (pointer-events: none) and sits over the map but under the HUD/panels. */
.lowhp-vignette {
  position: fixed; inset: 0; pointer-events: none; z-index: 15;
  background: radial-gradient(ellipse 78% 78% at center, rgba(150,12,12,0) 58%, rgba(150,12,12,.45) 100%);
  animation: lowhp-pulse 1.5s ease-in-out infinite;
}
@keyframes lowhp-pulse { 0%, 100% { opacity: .5; } 50% { opacity: .95; } }
.lowhp-vignette.hidden { display: none; }

/* DREAD — shown only while a legendary world encounter (The Wandering Relic) is on screen.
   Deliberately NOT a jump-scare: it's a cold violet dark closing in from the edges, breathing
   slowly, with a faint drifting interference over the top. `--dread` (0…1) is set every frame
   by monster-layer.js from the creature's real distance, so the world literally darkens as it
   comes toward you and eases off when it drifts away. Never blocks input. */
.dread-vignette {
  position: fixed; inset: 0; pointer-events: none; z-index: 16;
  opacity: var(--dread, 0);
  transition: opacity 700ms ease-out;
  background:
    radial-gradient(ellipse 62% 62% at center, rgba(8,4,18,0) 30%, rgba(10,4,24,.62) 72%, rgba(6,2,16,.92) 100%);
  animation: dread-breathe 5.5s ease-in-out infinite;
}
/* Faint interference drifting across the frame — the sense that something is wrong with the
   picture itself, rather than with the world. Cheap: one repeating gradient, translated. */
.dread-vignette::after {
  content: ''; position: absolute; inset: -50%;
  background: repeating-linear-gradient(
    112deg,
    rgba(190,170,255,.045) 0px, rgba(190,170,255,.045) 1px,
    rgba(0,0,0,0) 1px, rgba(0,0,0,0) 4px);
  animation: dread-drift 9s linear infinite;
  mix-blend-mode: screen;
}
@keyframes dread-breathe { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.35); } }
@keyframes dread-drift { 0% { transform: translate3d(0,0,0); } 100% { transform: translate3d(6%, 9%, 0); } }
.dread-vignette.hidden { display: none; }

/* Accessibility: honour the OS "reduce motion" setting — swap the continuous HUD throbs for
   calm static states (the red/green/vignette cues stay fully visible, just without the pulse). */
@media (prefers-reduced-motion: reduce) {
  #hp-bar.low, #hp-bar.healing, .pill.cl-up { animation: none !important; }
  #hp-bar.low { box-shadow: 0 0 8px 1px rgba(210,74,74,.5); }
  #hp-bar.healing { box-shadow: 0 0 8px 1px rgba(90,200,90,.45); }
  .lowhp-vignette { animation: none !important; opacity: .72; }
  /* Dread stays fully visible for players who asked for less motion — it just stops breathing
     and drifting. The darkness is the point; the movement is the garnish. */
  .dread-vignette { animation: none !important; }
  .dread-vignette::after { animation: none !important; opacity: .5; }
  .mm-here::after { display: none; } /* drop the expanding ping ring; the solid dot still marks you */
}
/* App-update prompts: the in-game modal's "Later" (a calm, full-width secondary button
   under the gold primary) and the sign-in gate's message shown inside the auth card. */
#appupdate-modal .appupdate-later {
  display: block; width: 100%; margin-top: 10px; padding: 11px;
  background: #1b2a1f; color: var(--muted); border: 1px solid #314733; border-radius: 10px;
  font-size: 15px; font-weight: 600; cursor: pointer;
}
#appupdate-modal .appupdate-later:active { background: #24352a; }
.auth-update-msg { color: var(--ink); line-height: 1.45; margin: 12px 0 14px; }
/* Full-screen out-of-date block (old app shells, shown instead of the game). */
#appblock-screen { display: flex; align-items: center; justify-content: center; padding: 20px; }
#appblock-screen.hidden { display: none; }
/* While the update gate is up, no path into a new session — including the Google button,
   which reveals itself async after the gate has run. */
.auth-card.auth-blocked #auth-form, .auth-card.auth-blocked .tabs,
.auth-card.auth-blocked #google-auth { display: none !important; }

/* Settings cog at the right end of the area/gold status line. The status bar ignores
   pointer events, so re-enable them here (like the boost pill). */
.settings-cog {
  pointer-events: auto; cursor: pointer; flex: none;
  width: clamp(34px, calc(8.4 * var(--u)), 64px); height: clamp(34px, calc(8.4 * var(--u)), 64px);
  background: none; border: none; padding: 0; line-height: 0;
}
.settings-cog svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 1px 1px rgba(0,0,0,.5)); }
.settings-cog:active { opacity: .65; }
/* Gold/coin marker. Drawn as an inline SVG rather than the 🪙 emoji (U+1FA99),
   which is missing from the emoji fonts on many Windows PCs and renders as tofu. */
.pill.coins::before {
  content: "";
  display: inline-block; vertical-align: -0.14em; margin-right: .35em;
  width: 1em; height: 1em;
  background: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Ccircle%20cx='12'%20cy='12'%20r='10'%20fill='%23e8c45a'%20stroke='%237a5a1c'%20stroke-width='1.5'/%3E%3Ccircle%20cx='12'%20cy='12'%20r='7'%20fill='none'%20stroke='%23a9761f'%20stroke-width='1'/%3E%3Cpath%20d='M12%208l1.25%202.55%202.8.4-2.02%201.95.48%202.8L12%2016.6l-2.5%201.3.48-2.8-2.02-1.95%202.8-.4z'%20fill='%23fff3c4'/%3E%3C/svg%3E") center/contain no-repeat;
}
/* Reusable inline coin glyph for shop/inventory/combat text, same art as the
   HUD pill above. Use in place of the 🪙 emoji which is tofu on many Windows PCs. */
.coin-ico {
  display: inline-block; vertical-align: -0.14em; width: 1em; height: 1em;
  background: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Ccircle%20cx='12'%20cy='12'%20r='10'%20fill='%23e8c45a'%20stroke='%237a5a1c'%20stroke-width='1.5'/%3E%3Ccircle%20cx='12'%20cy='12'%20r='7'%20fill='none'%20stroke='%23a9761f'%20stroke-width='1'/%3E%3Cpath%20d='M12%208l1.25%202.55%202.8.4-2.02%201.95.48%202.8L12%2016.6l-2.5%201.3.48-2.8-2.02-1.95%202.8-.4z'%20fill='%23fff3c4'/%3E%3C/svg%3E") center/contain no-repeat;
}
.pill.boost { color: #1a1306; background: linear-gradient(#e8c45a, #c8922f); border-color: #7a5a1c; font-weight: 700; }
.pill.boost.hidden { display: none; }
.s-region { font-weight: 600; color: var(--accent-2); }
/* Third status line: area name (+Safe badge) left, then gold and the settings cog on the right. */
.region-line .pill.coins { margin-left: auto; }

/* Dungeon items — the Ocarina-of-Time key/map/compass row (DUNGEON-03). Sits above the
   region line and only appears inside a dungeon that declares itemKeys. Unheld items stay
   visible but dimmed: seeing the boss key you have not found yet is the point. */
.dgn-items { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.dgn-items .dgn-item {
  font-size: 15px; line-height: 1; padding: 2px 3px; border-radius: 4px;
  opacity: 0.28; filter: grayscale(1);
  transition: opacity .18s ease, filter .18s ease, box-shadow .18s ease;
}
.dgn-items .dgn-item.held {
  opacity: 1; filter: none;
  background: rgba(255, 214, 120, 0.14);
  box-shadow: 0 0 0 1px rgba(255, 214, 120, 0.35) inset;
}
.dgn-items .dgn-room {
  margin-left: auto; font-size: 11px; letter-spacing: .02em;
  color: rgba(235, 226, 208, 0.72); text-transform: uppercase;
}
.s-coords {
  font-variant-numeric: tabular-nums; color: var(--muted); font-size: clamp(13px, calc(3.4 * var(--u)), 28px);
  background: #1b2a1fcc; border: 1px solid #314733; border-radius: 999px; padding: clamp(5px, calc(1.3 * var(--u)), 12px) clamp(13px, calc(3.2 * var(--u)), 28px);
}
.s-coords::before { content: "⌖ "; color: var(--accent); }
/* Distance-from-Rest readout: a subtle pill (like the other HUD chips) so it stays legible
   over any terrain — plain muted text used to wash out against bright/busy backgrounds. */
/* Sits directly beside the HP bar (margin-left:auto would push it to the right edge).
   The row never wraps — the HP bar shrinks instead (min 90px) so these always share a line. */
.hp-line { flex-wrap: nowrap; }
.s-dist { margin-left: 0; flex: none; white-space: nowrap; color: var(--muted); background: #11160fcc; border: 1px solid #2a3d2c;
  border-radius: 999px; padding: 2px clamp(9px, calc(2.2 * var(--u)), 14px); font-variant-numeric: tabular-nums; }
.s-dist #s-dist { color: var(--ink); font-weight: 600; }
/* Bottom-left HUD stack: anchors the safe-zone badge + beginner quest tracker to the same
   spot the mode controls use, but as a column so the badge sits ABOVE the tracker instead
   of covering it. Non-interactive; only the tracker's × opts back into pointer events. */
.ql-stack {
  position: absolute; z-index: 19;
  left: clamp(10px, calc(2.6 * var(--u)), 28px);
  bottom: calc(clamp(104px, calc(26 * var(--u)), 200px) + var(--safe-pad));
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
  pointer-events: none;
}
/* Safe-zone badge — only shown (JS toggles .hidden) while standing in a sanctuary. */
.s-safe {
  font-weight: 700; color: #7fd6a0; background: #14241acc; border: 1px solid #3f6e4f; border-radius: 999px; padding: 1px 9px; font-size: 0.86em;
}
.s-safe.hidden { display: none; }
/* Region-entry banner: a brief centred title that fades in/out when you cross into a new
   region. Non-interactive so it never blocks taps on the map underneath. */
.region-banner {
  position: absolute; top: 22%; left: 50%; transform: translate(-50%, -8px);
  pointer-events: none; text-align: center; z-index: 40;
  opacity: 0; transition: opacity .5s ease, transform .5s ease;
}
.region-banner.hidden { display: none; }
.region-banner.show { opacity: 1; transform: translate(-50%, 0); }
.region-banner .rb-name {
  display: block; font-weight: 800; color: var(--accent);
  font-size: clamp(26px, calc(7 * var(--u)), 56px);
  text-shadow: 0 2px 10px #000a, 0 0 22px rgba(0,0,0,.5); letter-spacing: .5px;
}

/* ---------------- Beginner quest tracker ---------------- */
.quest-tracker {
  /* Positioned by its parent .ql-stack (bottom-left, below the safe badge). */
  max-width: min(58vw, 300px);
  padding: clamp(7px, calc(1.7 * var(--u)), 14px) clamp(10px, calc(2.4 * var(--u)), 18px);
  background: linear-gradient(#171d12f2, #12160fdd);
  border: 1px solid #3c4a2e; border-left: 3px solid var(--accent);
  border-radius: 10px; box-shadow: 0 3px 14px #0007;
  /* Tappable: opens the Quests panel. This deliberately swallows taps in its own footprint
     (it used to be pointer-events:none so tap-to-move worked through it), because players
     could not reach the quest text any other way than ☰ → Quests — suggestion #1001593.
     It is a small bottom-left chip, so the lost walk-target area is minor. */
  pointer-events: auto; cursor: pointer;
  animation: qt-in .35s ease-out;
}
.quest-tracker:hover { border-color: #55683c; }
.quest-tracker:active { transform: translateY(1px); }
.quest-tracker.hidden { display: none; }
@keyframes qt-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.quest-tracker .qt-head { display: flex; align-items: baseline; gap: 6px; }
.quest-tracker .qt-scroll { font-size: clamp(12px, calc(3 * var(--u)), 16px); }
.quest-tracker .qt-title {
  font-weight: 800; color: var(--accent); letter-spacing: .3px;
  font-size: clamp(12px, calc(3 * var(--u)), 16px);
}
.quest-tracker .qt-step { margin-left: auto; color: var(--muted); font-size: clamp(10px, calc(2.4 * var(--u)), 13px); font-weight: 600; }
.quest-tracker .qt-objective {
  color: var(--ink); margin-top: 3px; line-height: 1.25;
  font-size: clamp(12px, calc(2.9 * var(--u)), 15px);
}
.quest-tracker .qt-guide {
  color: var(--muted); margin-top: 2px; line-height: 1.2;
  font-size: clamp(10px, calc(2.4 * var(--u)), 13px);
}
/* The panel ignores pointer events (so it never eats map taps); only the × is interactive.
   Styled as a small round chip in the tracker's own woodland palette so it reads as a
   button rather than a stray glyph, and lit with the gold accent on hover/press. */
.quest-tracker .qt-hide {
  pointer-events: auto; cursor: pointer; margin-left: 8px; align-self: center; flex: 0 0 auto;
  position: relative; display: flex; align-items: center; justify-content: center;
  width: clamp(21px, calc(5.2 * var(--u)), 28px); height: clamp(21px, calc(5.2 * var(--u)), 28px);
  border-radius: 50%; border: 1px solid #3c4a2e;
  background: linear-gradient(#1e2617, #141a0f); color: var(--muted);
  font-size: clamp(14px, calc(3.4 * var(--u)), 18px); line-height: 1; padding: 0;
  box-shadow: inset 0 1px 0 rgba(233, 226, 190, .06), 0 1px 3px #0006;
  transition: color .12s ease, background .12s ease, border-color .12s ease, transform .05s ease;
}
/* The chip stays visually small next to the quest title, but the tap target reaches a
   comfortable thumb size. Its click handler stopPropagation()s so dismissing the tracker
   never also opens the Quests panel behind it. */
.quest-tracker .qt-hide::after { content: ''; position: absolute; inset: -8px; border-radius: 50%; }
.quest-tracker .qt-hide:hover {
  color: var(--ink); background: linear-gradient(#2a3420, #1c2414); border-color: var(--accent);
}
.quest-tracker .qt-hide:active { transform: translateY(1px); box-shadow: inset 0 1px 3px #0008; }
.quest-tracker .qt-hide:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Quests modal (☰ → Quests) */
#quest-modal .qm-title { font-weight: 800; color: var(--accent); font-size: 16px; }
#quest-modal .qm-step { color: var(--muted); font-size: 12px; margin-top: 2px; }
#quest-modal .qm-obj { color: var(--ink); margin-top: 8px; line-height: 1.3; }
#quest-modal .qm-guide { color: var(--muted); margin-top: 4px; line-height: 1.25; font-size: 13px; }
#quest-modal .qm-empty { color: var(--muted); line-height: 1.35; }
#quest-modal #quest-toggle { margin-top: 14px; }
#quest-modal #quest-close { margin-top: 8px; }

/* ---------------- Map controls ---------------- */
#map-controls {
  position: absolute; right: clamp(12px, calc(3 * var(--u)), 28px);
  bottom: calc(clamp(104px, calc(26 * var(--u)), 200px) + var(--safe-pad)); z-index: 20;
  display: flex; flex-direction: column; gap: clamp(12px, calc(3 * var(--u)), 24px); align-items: flex-end;
}
.map-btn {
  width: clamp(68px, calc(17 * var(--u)), 130px); height: clamp(68px, calc(17 * var(--u)), 130px); border-radius: clamp(16px, calc(4 * var(--u)), 28px);
  background: #1b2a1fe6; border: 1px solid #314733; color: var(--ink);
  font-size: clamp(30px, calc(7.6 * var(--u)), 58px); cursor: pointer;
}
.map-btn.wide { width: auto; padding: 0 clamp(20px, calc(4.5 * var(--u)), 40px); font-size: clamp(20px, calc(5 * var(--u)), 40px); height: clamp(58px, calc(14 * var(--u)), 100px); }
.map-btn.wide#btn-mode {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-width: clamp(112px, calc(28 * var(--u)), 220px); line-height: 1.05;
}
#btn-mode .mode-live { display: block; font-weight: 800; }
#btn-mode small {
  display: block; margin-top: 2px; color: var(--muted); font-weight: 700;
  font-size: clamp(11px, calc(2.8 * var(--u)), 22px); white-space: nowrap;
}
.map-btn:active { background: var(--panel-2); }
/* Hidden while the (transparent) minimap is open so the buttons don't bleed through it. */
#map-controls.mm-hide { display: none; }
#btn-install { background: linear-gradient(#d8b25a, #b8862f); color: #1a1306; border-color: #7a5a1c; font-weight: 700; }
#btn-install:active { background: #c8922f; }

/* ---------------- Music controls popover ---------------- */
.music-pop {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: 92px; z-index: 35;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  background: #1b2a1ff2; border: 1px solid #314733; border-radius: 12px; padding: 12px 14px;
  box-shadow: 0 8px 22px rgba(0,0,0,.5); width: 220px;
}
.music-pop.hidden { display: none; }
/* Sound settings popover anchored under the top-left cog rather than centred. */
.settings-pop {
  left: clamp(14px, calc(3.6 * var(--u)), 36px); transform: none;
  top: calc(env(safe-area-inset-top, 0px) + clamp(96px, calc(24 * var(--u)), 200px)); bottom: auto;
}

/* ---------------- Menu popover (☰: News, Hall of Wanderers, World Map) ---------------- */
.menu-pop {
  position: absolute; left: 50%; transform: translateX(-50%); z-index: 36;
  bottom: calc(var(--safe-pad) + clamp(120px, calc(30 * var(--u)), 220px));
  display: flex; flex-direction: column; gap: 8px;
  background: #1b2a1ff7; border: 1px solid #314733; border-radius: 14px; padding: 10px;
  box-shadow: 0 10px 26px rgba(0,0,0,.55); min-width: clamp(220px, calc(56 * var(--u)), 360px);
}
.menu-pop.hidden { display: none; }
.menu-item {
  display: flex; align-items: center; gap: 8px; text-align: left; white-space: nowrap;
  padding: clamp(12px, calc(3 * var(--u)), 20px) clamp(14px, calc(3.5 * var(--u)), 22px); border-radius: 10px;
  background: #0e130d; border: 1px solid #314733; color: var(--ink); cursor: pointer;
  font-size: clamp(16px, calc(4.2 * var(--u)), 28px);
}
.menu-item:active { background: var(--panel-2); }
/* Unread-news dot: a small red pip on the ☰ button and beside the News item. */
.menu-dot {
  position: absolute; top: 8px; right: 8px; width: 13px; height: 13px; border-radius: 50%;
  background: #e8503a; border: 2px solid #1b2a1f; box-shadow: 0 0 6px #e8503a;
}
.menu-dot.inline { position: static; width: 11px; height: 11px; border-width: 0; margin-left: 4px; }
.menu-dot.hidden { display: none; }
#btn-menu { position: relative; }

/* News modal content */
.news-card { text-align: left; }
/* Keep Close on screen however long the news gets. .modal-card scrolls its own content, so
   a button at the end of that content scrolls away with it: on an 874pt iPhone the News
   Close button rendered at y≈1093, ~220pt below the fold, and nothing about the panel says
   "scroll for the exit". Sticking it to the bottom of the scrollport costs nothing on a
   desktop (where it never overflows) and makes the panel dismissible in one tap on a phone.
   The negative margins bleed it to the card's edges, cancelling .modal-card's 20px padding;
   the gradient stops list items showing through as they scroll underneath. */
.news-actions {
  position: sticky; bottom: -20px;
  margin: 14px -20px -20px; padding: 12px 20px 20px;
  background: linear-gradient(180deg, rgba(27, 42, 31, 0) 0%, rgba(27, 42, 31, .96) 45%);
}
.news-actions #news-close { width: 100%; }
.news-item { border: 1px solid #314733; border-left-width: 4px; border-radius: 10px; background: #0e130d; padding: 11px 13px; margin-bottom: 10px; }
.news-item.featured { border-left-color: #d98fd5; background: #170f17; }
.news-item h3 { margin: 0 0 4px; font-size: clamp(16px, calc(4.4 * var(--u)), 28px); color: var(--accent); }
.news-item.featured h3 { color: #f0a8ec; }
.news-item p { margin: 0; font-size: clamp(13px, calc(3.6 * var(--u)), 24px); color: var(--ink); line-height: 1.4; }
.news-item .news-date { color: var(--muted); font-size: clamp(11px, calc(3 * var(--u)), 20px); }
.news-item a { color: #f0a8ec; text-decoration: underline; word-break: break-all; }
.guide-intro { margin: 0 0 12px; color: var(--muted); font-size: clamp(12px, calc(3.4 * var(--u)), 22px); }
/* Guides modal: an index of skills, each opening a full page. */
.guide-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.guide-item { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 14px; border: 1px solid #314733; border-radius: 10px; background: #0e130d;
  color: var(--ink); cursor: pointer; }
.guide-item:hover, .guide-item:active { border-color: var(--accent); background: #131a10; }
.guide-item-icon { font-size: clamp(22px, calc(6 * var(--u)), 40px); line-height: 1; }
.guide-item-text { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.guide-item-text strong { color: var(--accent); font-size: clamp(16px, calc(4.4 * var(--u)), 28px); }
.guide-item-text small { color: var(--muted); font-size: clamp(12px, calc(3.2 * var(--u)), 22px); }
.guide-item-arrow { color: var(--muted); font-size: clamp(20px, calc(5 * var(--u)), 34px); }
.guide-back { background: none; border: none; color: var(--accent); cursor: pointer;
  padding: 0 0 8px; font-size: clamp(14px, calc(3.6 * var(--u)), 24px); }
.guide-page h4 { margin: 16px 0 6px; color: var(--accent); font-size: clamp(15px, calc(4 * var(--u)), 26px); }
.guide-page p { margin: 0 0 10px; line-height: 1.45; font-size: clamp(13px, calc(3.6 * var(--u)), 24px); color: var(--ink); }
.guide-page ul { margin: 0 0 10px; padding-left: 20px; }
.guide-page li { margin: 0 0 5px; line-height: 1.4; font-size: clamp(13px, calc(3.6 * var(--u)), 24px); color: var(--ink); }
.guide-page strong { color: #e8d9a0; }
.music-pop .mp-title { font-weight: 700; color: var(--accent); font-size: 13px; }
.music-pop .mp-mute {
  width: 100%; padding: 7px; border-radius: 8px; border: 1px solid #314733;
  background: var(--panel-2); color: var(--ink); cursor: pointer; font-size: 13px; font-weight: 600;
}
.music-pop .mp-mute.active { border-color: var(--accent-2); color: var(--accent-2); }
.music-pop .mp-logout {
  width: 100%; padding: 7px; border-radius: 8px; border: 1px solid #6a3030;
  background: var(--panel-2); color: #e08070; cursor: pointer; font-size: 13px; font-weight: 600;
}
.music-pop .mp-logout:disabled { opacity: .5; cursor: default; }
.music-pop .mp-account { display: flex; flex-direction: column; align-items: center; gap: 6px; color-scheme: light; }
.music-pop .mp-account #link-google-host { display: flex; justify-content: center; min-height: 0; }
.music-pop .mp-linked { font-size: 12px; color: var(--accent-2, #7fd07f); font-weight: 600; }
.music-pop .mp-account-err { font-size: 11px; color: #e08070; text-align: center; }

/* Google link conflict: compare two characters side by side. */
.link-conflict-card { max-width: 460px; }
.link-compare { display: flex; gap: 10px; margin: 12px 0; }
.link-compare .lc-col { flex: 1 1 0; min-width: 0; background: var(--panel-2); border: 1px solid #ffffff14;
  border-radius: 10px; padding: 10px; }
.link-compare .lc-current { border-color: var(--accent-2, #7fd07f); }
.lc-head { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.lc-badge { background: #ffffff1a; border-radius: 6px; padding: 1px 5px; font-size: 10px; letter-spacing: 0; }
.lc-name { font-size: 16px; font-weight: 700; color: var(--accent); margin: 2px 0 8px; overflow-wrap: anywhere; }
.lc-grid { display: grid; grid-template-columns: auto 1fr; gap: 3px 8px; font-size: 12px; }
.lc-grid .lc-k { color: var(--muted); }
.lc-grid .lc-v { text-align: right; font-variant-numeric: tabular-nums; }
.lc-warn { font-size: 12px; color: #e8b54a; background: #e8b54a18; border-radius: 8px; padding: 8px 10px; }
.lc-actions { display: flex; gap: 10px; margin-top: 12px; }
.lc-actions button { flex: 1 1 0; }
.music-pop .mp-links { display: flex; justify-content: center; gap: 14px; font-size: 11px; }
.music-pop .mp-links a { color: var(--muted); text-decoration: none; }
.music-pop .mp-links a:hover { color: var(--accent); text-decoration: underline; }

/* ---------------- Tap-a-player inspect popup ---------------- */
.inspect-pop {
  position: absolute; z-index: 27; width: 168px;
  background: #1b2a1ff5; border: 1px solid #314733; border-radius: 10px;
  padding: 9px 11px; box-shadow: 0 8px 22px rgba(0,0,0,.5);
  color: var(--ink); text-align: center;
}
.inspect-pop.hidden { display: none; }
.inspect-pop .ip-name { font-weight: 700; font-size: 14px; }
.inspect-pop .ip-sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
/* Foe combat-level threat colour vs your own level (set in game.js _showInspect). */
.ip-lvl { font-weight: 700; }
.ip-lvl.threat-easy { color: #8fd07a; }   /* well below you — easy */
.ip-lvl.threat-even { color: #f0d68a; }   /* near your level — fair fight */
.ip-lvl.threat-hard { color: #f08a7a; }   /* well above you — dangerous */
.ip-attack {
  margin-top: 8px; width: 100%; height: 34px; border: none; border-radius: 8px;
  background: linear-gradient(#b6432e, #8d2f20); color: #fff; font-weight: 700;
  font-size: 14px; cursor: pointer;
}
.ip-attack:active { filter: brightness(1.15); }
.ip-attack.hidden { display: none; }

/* ---------------- Chat ---------------- */
#btn-chat { position: relative; }
.chat-badge {
  position: absolute; top: -4px; right: -4px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 999px; background: var(--accent); color: #11160f;
  font-size: 11px; font-weight: 700; line-height: 16px; text-align: center;
}
.chat-badge.hidden { display: none; }
.chat {
  position: absolute; left: 8px; right: 8px; bottom: calc(clamp(80px, calc(19 * var(--u)), 160px) + var(--safe-pad));
  /* 30, well above #quick-slots (26): the slots share this exact `bottom` offset, so they sit
     inside the chat window's lower edge. At an equal z-index the slots won on DOM order alone
     (they come later in index.html) and covered the newest messages. #bottom-nav is also 30 and
     still wins the tie the same way, which is right — chat stops above the nav. */
  z-index: 30; height: 44%; max-height: 360px; display: flex; flex-direction: column;
  background: #11160ff5; border: 1px solid #314733; border-radius: 12px 12px 0 0;
  box-shadow: 0 -8px 24px rgba(0,0,0,.5); overflow: hidden;
}
.chat.hidden { display: none; }
.chat-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 10px; border-bottom: 1px solid #314733; font-weight: 700; color: var(--accent);
  font-size: clamp(18px, calc(4.8 * var(--u)), 32px);
}
/* Channel tabs (General / Bug Reports / Suggestions). Sized well under the head's title
   scale; scrolls horizontally on the narrowest phones rather than wrapping. */
.chat-tabs { display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none; min-width: 0; }
.chat-tabs::-webkit-scrollbar { display: none; }
.chat-tab {
  position: relative; flex: 0 0 auto; padding: 6px 12px; border-radius: 8px;
  background: transparent; border: 1px solid transparent; color: var(--muted);
  font: inherit; font-weight: 700; font-size: clamp(14px, calc(3.6 * var(--u)), 22px);
  cursor: pointer; white-space: nowrap;
}
.chat-tab.active { background: #1c2618; border-color: #314733; color: var(--accent); }
.chat-tab-dot {
  position: absolute; top: 3px; right: 3px; width: 8px; height: 8px; border-radius: 50%;
  background: #e8503a; box-shadow: 0 0 4px #e8503a;
}
.chat-tab-dot.hidden { display: none; }
.chat-log { flex: 1; overflow-y: auto; padding: 8px 12px; display: flex; flex-direction: column; gap: 5px; }
.chat-msg { font-size: clamp(15px, calc(4.2 * var(--u)), 28px); line-height: 1.35; word-break: break-word; }
.chat-msg b { color: var(--accent-2); margin-right: 4px; }
.chat-msg.mine b { color: var(--accent); }
.chat-name { cursor: pointer; }
.chat-name:active { text-decoration: underline; }
/* Bridged from Discord: blurple name, no profile tap. */
.chat-msg b.chat-discord { color: #8b9dff; cursor: default; }
.chat-msg b.chat-discord:active { text-decoration: none; }
.chat-time { color: var(--muted); font-size: 0.74em; opacity: .65; margin-right: 5px; font-variant-numeric: tabular-nums; }
/* Moderator warning: red, bordered, stands apart from player chatter. */
.chat-msg.chat-mod { color: #ff6b5e; background: #2a14129a; border-left: 3px solid #e8503a; border-radius: 6px; padding: 4px 8px; margin: 3px 0; font-weight: 600; }
.chat-msg.chat-mod b { color: #ff8a7e; }
.chat-lv { color: var(--muted); font-size: 0.82em; opacity: .85; margin-right: 4px; font-weight: 700; }
/* "auto translated" tag on machine-translated lines: quiet, italic, set apart from the words. */
.chat-trtag { color: var(--muted); font-size: 0.72em; font-style: italic; opacity: .6; white-space: nowrap; }
.chat-trtag::before { content: "· "; }
/* Flag glyph in the Language settings button/title. */
.lang-flag { font-size: 1.05em; }
/* Level-band tint (echoes the map's monster threat palette: green→tan→orange→red). */
.chat-lv.lv-new { color: #9ad06a; }
.chat-lv.lv-mid { color: #e6b6a6; }
.chat-lv.lv-vet { color: #f0a24a; }
.chat-lv.lv-elite { color: #ff8a3c; }
.chat-typing {
  padding: 0 12px 4px; min-height: 1.1em; font-style: italic; color: #9bb08a;
  font-size: clamp(12px, calc(3.2 * var(--u)), 19px); opacity: 0.85;
}
.chat-typing.hidden { display: none; }
/* "New messages" chip: appears when a line arrives while the reader is scrolled up; tap to jump. */
.chat-jump {
  align-self: center; margin: 0 0 4px; padding: 5px 14px; border-radius: 999px; cursor: pointer;
  border: 1px solid #6a4f1c; background: linear-gradient(#d8b25a, #b8862f); color: #1a1306; font-weight: 700;
  font-size: clamp(12px, calc(3.2 * var(--u)), 20px); box-shadow: 0 3px 10px rgba(0,0,0,.4);
}
.chat-jump.hidden { display: none; }
.chat-form { display: flex; gap: 6px; padding: 8px; border-top: 1px solid #314733; }
.chat-form input {
  flex: 1; min-width: 0; height: clamp(42px, calc(11 * var(--u)), 64px); padding: 0 14px; border-radius: 8px;
  border: 1px solid #314733; background: #0e130d; color: var(--ink); font-size: clamp(16px, calc(4.2 * var(--u)), 28px);
}
.chat-send {
  height: clamp(42px, calc(11 * var(--u)), 64px); padding: 0 clamp(16px, calc(4.4 * var(--u)), 30px); border: none; border-radius: 8px;
  background: var(--accent); color: #11160f; font-weight: 700; cursor: pointer; font-size: clamp(16px, calc(4.2 * var(--u)), 28px);
}

/* ---------------- Notifications ---------------- */
/* Toasts hang below the status bar. Its height is fluid — it grows with --u, wraps on
   narrow screens, and gains a line when the boost/safe pills show — so a fixed offset put
   the "you defeated X" toast straight on top of the HP pill. notifications.js measures the
   real header and publishes it as --hud-h; the fallback only applies before that runs.
   --hud-h already includes the bar's safe-area padding, so no env() here. */
#notifications {
  position: absolute; left: 50%; transform: translateX(-50%);
  top: calc(var(--hud-h, calc(env(safe-area-inset-top, 0px) + 78px)) + 8px);
  width: min(94%, 620px); z-index: 25;
  display: flex; flex-direction: column; gap: 6px; pointer-events: none;
}
.toast {
  background: #1b2a1fee; border: 1px solid #314733; border-left: 4px solid var(--accent);
  border-radius: 8px; padding: clamp(11px, calc(2.8 * var(--u)), 20px) clamp(14px, calc(3.4 * var(--u)), 26px); font-size: clamp(16px, calc(4.4 * var(--u)), 30px);
  box-shadow: 0 6px 18px rgba(0,0,0,.4);
  animation: toast-in .25s ease, toast-out .4s ease 4.6s forwards;
}
.toast.good { border-left-color: var(--accent-2); }
.toast.warn { border-left-color: var(--danger); }
/* Loot toasts emphasised by rarity (colours match the inventory rarity palette). Common
   tiers (worn/standard/refined) keep the plain 'good' look; Rare+ get colour, Epic/Relic glow. */
.toast.loot-rare  { border-left-color: #5a86c0; }
.toast.loot-epic  { border-left-color: #b46fd0; border-color: #4a335a; box-shadow: 0 6px 18px rgba(0,0,0,.4), 0 0 14px rgba(180,111,208,.45); }
.toast.loot-relic { border-left-color: #d8a24a; border-color: #6a5326; box-shadow: 0 6px 18px rgba(0,0,0,.4), 0 0 18px rgba(216,162,74,.6); }
/* Discovery toasts: map-blue for a personal first visit; gold + glow for a global-first find. */
.toast.discover { border-left-color: #5f9bd8; }
.toast.first { border-left-color: #d8a24a; border-color: #6a5326; box-shadow: 0 6px 18px rgba(0,0,0,.4), 0 0 18px rgba(216,162,74,.6); font-weight: 600; }
@keyframes toast-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(-8px); } }

/* Server-wide announcements (boss kills, Relic finds). Centered, bolder, held ~2.5x longer than a
   normal toast (the out-animation is delayed to ~12.5s to match notifications.js ANNOUNCE_LIFE_MS),
   with a soft pulsing glow and a few drifting sparks. Variant colour is carried by --glow. */
.toast.announce {
  text-align: center; font-weight: 700; letter-spacing: .2px; position: relative; overflow: visible;
  border-left-width: 1px; background: #12161dee;
  animation: toast-in .3s ease, toast-out .5s ease 12.4s forwards;
}
.toast.announce::before {
  content: ''; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 22px 2px var(--glow, #fff); opacity: .3;
  animation: announce-pulse 2.2s ease-in-out infinite;
}
.toast.announce-boss  { color: #c3e6ff; border-color: #2f4a63; --glow: rgba(120,190,255,.6);  text-shadow: 0 0 9px rgba(120,190,255,.55); }
.toast.announce-relic { color: #ffe6a3; border-color: #6a5326; --glow: rgba(255,208,110,.65); text-shadow: 0 0 9px rgba(255,208,110,.6); }
.toast.announce .spark {
  position: absolute; bottom: 8px; width: 4px; height: 4px; border-radius: 50%;
  background: var(--glow, #fff); box-shadow: 0 0 7px 1px var(--glow, #fff);
  opacity: 0; pointer-events: none; animation: spark-float 2.4s ease-in infinite;
}
@keyframes announce-pulse { 0%, 100% { opacity: .22; } 50% { opacity: .6; } }
@keyframes spark-float {
  0%   { opacity: 0; transform: translateY(0) scale(.5); }
  18%  { opacity: .95; }
  100% { opacity: 0; transform: translateY(-40px) scale(1); }
}

/* ---------------- Consumable quick slots (2, right above the bottom nav) ---------------- */
/* 26 keeps the slots under every window that opens over the bottom of the screen — .panel and
   .minimap (28), the inspect popup (27) and .chat (30) — since they share the panels' `bottom`
   offset and would otherwise float on top of an open Character/Skills/Shop window. */
#quick-slots {
  position: absolute; left: 0; right: 0; z-index: 26;
  bottom: calc(clamp(80px, calc(19 * var(--u)), 160px) + var(--safe-pad));
  display: flex; justify-content: center; gap: clamp(10px, calc(2.6 * var(--u)), 18px);
  pointer-events: none; /* the bar itself is a layout box; only its buttons are tappable */
}
.qs-slot { position: relative; pointer-events: none; width: clamp(46px, calc(12 * var(--u)), 64px); height: clamp(46px, calc(12 * var(--u)), 64px); }
.qs-main {
  width: 100%; height: 100%; border-radius: 10px; pointer-events: auto;
  background: #161d14; border: 1px solid #243024; color: var(--muted);
  display: flex; align-items: center; justify-content: center;
  font-size: clamp(22px, calc(6 * var(--u)), 32px); padding: 3px; cursor: pointer;
}
.qs-main.assigned { border-color: #314733; }
.qs-main img { width: 100%; height: 100%; object-fit: contain; image-rendering: pixelated; }
.qs-qty {
  position: absolute; top: -10px; left: 50%; transform: translateX(-50%); pointer-events: none;
  font-size: 11px; font-weight: 800; color: var(--fg); background: #11160fef;
  padding: 1px 6px; border-radius: 999px; white-space: nowrap; border: 1px solid #314733;
}
.qs-x, .qs-toggle, .qs-settings {
  position: absolute; width: 19px; height: 19px; border-radius: 50%; padding: 0;
  pointer-events: auto; cursor: pointer; font-size: 10px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.qs-x { top: -6px; right: -6px; background: var(--danger); color: #fff; border: none; }
/* Auto-use toggle and its settings cog are the two the thumb actually aims for, so they get a
   noticeably larger target than the little remove "x" above them. */
.qs-toggle, .qs-settings {
  width: clamp(26px, calc(6.6 * var(--u)), 36px); height: clamp(26px, calc(6.6 * var(--u)), 36px);
  font-size: clamp(14px, calc(3.6 * var(--u)), 19px);
}
.qs-toggle { bottom: -8px; left: -8px; background: #11160f; border: 1px solid #314733; color: var(--muted); }
.qs-toggle.on { background: var(--accent); border-color: var(--accent); color: #11160f; }
.qs-settings { bottom: -8px; right: -8px; background: #11160f; border: 1px solid #314733; color: var(--muted); }

/* Auto-use settings popover content (reuses the shared .modal/.modal-card chrome). */
.qs-settings-group { margin-bottom: 14px; }
.qs-settings-label { font-size: 13px; color: var(--muted); margin-bottom: 6px; }
.qs-settings-row { display: flex; gap: 8px; flex-wrap: wrap; }
.qs-opt {
  flex: 1; min-width: 64px; padding: 8px 10px; border-radius: 8px;
  background: #161d14; border: 1px solid #243024; color: var(--muted); cursor: pointer; font-size: 13px;
}
.qs-opt.active { background: #1c2618; border-color: var(--accent); color: var(--accent); }

/* ---------------- Bottom navigation ---------------- */
#bottom-nav {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 30;
  display: flex; background: #11160ff2; border-top: 1px solid #314733;
  padding-bottom: var(--safe-pad);
}
.nav-btn {
  flex: 1; position: relative; background: transparent; border: none; color: var(--muted);
  padding: clamp(14px, calc(3.4 * var(--u)), 28px) 0 clamp(13px, calc(3.2 * var(--u)), 26px); font-size: clamp(33px, calc(8.4 * var(--u)), 64px); cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: clamp(4px, calc(1 * var(--u)), 9px);
}
.nav-btn span { font-size: clamp(16px, calc(4 * var(--u)), 30px); }
.nav-btn.active { color: var(--accent); }
/* "Pack full" warning dot on the Inventory nav button (drops auto-sell when full). */
.nav-badge {
  position: absolute; top: clamp(6px, calc(1.6 * var(--u)), 14px); right: 28%;
  min-width: clamp(16px, calc(4 * var(--u)), 26px); height: clamp(16px, calc(4 * var(--u)), 26px);
  border-radius: 999px; background: var(--danger); color: #fff; font-weight: 800;
  font-size: clamp(11px, calc(2.8 * var(--u)), 18px); line-height: clamp(16px, calc(4 * var(--u)), 26px);
  text-align: center; box-shadow: 0 0 6px rgba(210,74,74,.7);
}
.nav-badge.hidden { display: none; }

/* ---------------- Slide-up panel ---------------- */
/* WIDTH. This is a phone-first bottom sheet, and left:0/right:0 let it span whatever screen it
   found: on a tablet or desktop the Inventory grid ran fifteen slots across, and Shop rows put
   their name against the far left edge and their buy button against the far right with a lake of
   empty panel in between. It now stops growing past a comfortable reading width and centres
   instead — phones are unaffected (min() picks 100%). The slide-in transform carries the centring
   too, since both states share the transform property. */
.panel {
  position: absolute; left: 50%; right: auto;
  width: min(100%, 760px);
  bottom: calc(clamp(80px, calc(19 * var(--u)), 160px) + var(--safe-pad)); z-index: 28;
  max-height: 60%; background: var(--panel);
  border-top: 1px solid #314733; border-radius: 16px 16px 0 0;
  box-shadow: 0 -10px 30px rgba(0,0,0,.5);
  transform: translate(-50%, 100%); transition: transform .25s ease;
  display: flex; flex-direction: column;
}
.panel:not(.hidden) { transform: translate(-50%, 0); }
/* Once it stops touching the screen edges it reads as a card, so round the bottom corners too. */
@media (min-width: 800px) { .panel { border-radius: 16px; border: 1px solid #314733; } }
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 18px; border-bottom: 1px solid #2a3d2c; font-weight: 700; font-size: clamp(20px, calc(5.4 * var(--u)), 40px);
}
/* Pack fullness beside the "Inventory" title — lighter than the title, red once full. */
.panel-title-cap { margin-left: 10px; color: var(--muted); font-weight: 400; font-size: clamp(15px, calc(4 * var(--u)), 28px); }
.panel-title-cap.full { color: #f87171; font-weight: 700; }
.panel-close { background: none; border: none; color: var(--muted); font-size: clamp(28px, calc(7 * var(--u)), 50px); cursor: pointer; }
/* overflow-anchor:none stops the browser nudging the scroll position when the list is rebuilt
   under the viewport (a live update riding in with each toast) — otherwise it creeps down a bit. */
.panel-body { padding: 16px 18px 24px; overflow-y: auto; overflow-anchor: none; font-size: clamp(18px, calc(4.8 * var(--u)), 34px); }

/* Thin progress bar + small status chips — retained for the compact Skills list. */
.ubar { height: clamp(6px, calc(1.5 * var(--u)), 9px); background: #0c110b; border-radius: 5px; overflow: hidden; box-shadow: inset 0 0 0 1px #223528; }
.ubar > i { display: block; height: 100%; background: linear-gradient(90deg, var(--accent-2), var(--accent)); border-radius: 5px; }
.wchip { display: inline-block; padding: 1px clamp(6px,calc(1.7*var(--u)),9px); border-radius: 999px; font-size: clamp(10px, calc(2.9 * var(--u)), 16px); font-weight: 800; letter-spacing: .02em; vertical-align: middle; white-space: nowrap; }
.wchip.warn { background: #3a1c1c; color: #ffb1a3; border: 1px solid #5a2c25; }
.wchip.ok   { background: #14241a; color: #8fd6a3; border: 1px solid #3f6e4f; }
.wchip.info { background: #1b2536; color: #9db8e8; border: 1px solid #33456a; }
.wchip.gold { background: #2c2410; color: #f0c878; border: 1px solid #6a4f1c; }

/* Character/Shop text is sized off the SAME scale as the Skills and pack rows (.sk-name /
   .sk-xp), so every panel in the game reads at one size. Body text clamp(14,3.9u,24),
   secondary clamp(11,3u,17), section headings clamp(12,3.2u,20). Change them together. */
.stat-row { display: flex; justify-content: space-between; padding: clamp(6px,calc(1.7 * var(--u)),10px) 0;
  border-bottom: 1px solid #223528; font-size: clamp(14px, calc(3.9 * var(--u)), 24px); }
.stat-row span:last-child { color: var(--accent); font-weight: 600; }
.my-profile-btn {
  display: block; width: 100%; margin: 10px 0 4px; padding: clamp(8px, calc(2.1 * var(--u)), 13px);
  border-radius: 10px; border: 1px solid #4a5a3a; background: var(--panel-2); color: var(--accent-2);
  cursor: pointer; font-weight: 700; font-size: clamp(13px, calc(3.5 * var(--u)), 20px);
}
.my-profile-btn:active { background: #2a3a22; }
.logout-btn {
  display: block; width: 100%; margin-top: 20px; padding: clamp(14px, calc(3.6 * var(--u)), 26px);
  border: 1px solid #5a2a2a; border-radius: 10px; background: #2a1a1a;
  color: #e7b3b3; font-weight: 600; font-size: clamp(17px, calc(4.6 * var(--u)), 32px); cursor: pointer;
}
.logout-btn:hover { background: #3a2020; }
.logout-btn:disabled { opacity: 0.6; cursor: default; }
/* Gold-fed variant of the shared thin progress bar (the combat-focus skill). */
.ubar.gold { box-shadow: inset 0 0 0 1px #6a4f1c, 0 0 6px 0 rgba(232,196,90,.35); }
.ubar.gold > i { background: linear-gradient(90deg, #e8c45a, #f0d27a); }
.placeholder { color: var(--muted); text-align: center; padding: 24px 8px; }

/* ---------------- Skills list (compact rows) ---------------- */
.sk-group { color: var(--muted); font-size: clamp(11px, calc(3 * var(--u)), 17px); text-transform: uppercase;
  letter-spacing: .06em; font-weight: 700; margin: 10px 0 7px; }
.sk-group:first-child { margin-top: 0; }
/* Group header that carries the Gathering & Crafting sort control on its right. */
.sk-group-sortable { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.sk-sort { display: inline-flex; align-items: center; gap: 6px; text-transform: none; letter-spacing: 0;
  font-weight: 600; color: var(--muted); font-size: clamp(10px, calc(2.7 * var(--u)), 15px); }
.sk-sort select { background: #1a2212; color: #e8d9a0; border: 1px solid #4a5a3a; border-radius: 6px;
  padding: clamp(3px,calc(1 * var(--u)),7px) clamp(6px,calc(1.6 * var(--u)),12px);
  font-size: clamp(11px, calc(2.9 * var(--u)), 16px); font-family: inherit; cursor: pointer; }
.sk-row { display: flex; align-items: center; gap: 10px; padding: clamp(7px,calc(1.9*var(--u)),11px) 0; border-bottom: 1px solid #1e2c20; }
.sk-badge { flex-shrink: 0; width: clamp(30px,calc(8.2*var(--u)),44px); height: clamp(30px,calc(8.2*var(--u)),44px);
  display: flex; align-items: center; justify-content: center; border-radius: 9px; font-weight: 900;
  font-size: clamp(15px,calc(4*var(--u)),24px); color: #11160f; background: var(--sc);
  box-shadow: inset 0 1px 1px rgba(255,255,255,.3), inset 0 -2px 4px rgba(0,0,0,.28); }
.sk-mid { flex: 1; min-width: 0; }
.sk-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 5px; }
.sk-name { font-weight: 700; font-size: clamp(14px, calc(3.9 * var(--u)), 24px); }
.sk-focus { margin-left: 6px; }
.sk-lvl { color: var(--muted); font-size: clamp(12px, calc(3.2 * var(--u)), 20px); white-space: nowrap; }
.sk-lvl b { color: var(--accent); font-weight: 800; font-size: 1.15em; }
.sk-xp { color: var(--muted); font-size: clamp(11px, calc(3 * var(--u)), 17px); margin-top: 4px; }

/* ---- Minimap overlay (a 1:10 live-world Phaser camera renders into the body) ---- */
.minimap {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 95%; z-index: 28;
  /* Transparent: the Phaser minimap camera renders into the single game canvas BEHIND this
     box, so the box (and its body) must be see-through for the terrain to show. Only the
     head carries a fill. */
  background: transparent; border: 1px solid #314733; border-radius: 14px;
  box-shadow: 0 8px 30px rgba(0,0,0,.55); overflow: hidden;
}
.minimap.hidden { display: none; }
.minimap-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid #2a3d2c; font-weight: 700;
  font-size: clamp(18px, calc(4.8 * var(--u)), 34px);
  background: var(--panel);
}
/* Transparent: the Phaser minimap camera draws the terrain behind this rectangle. */
.minimap-body {
  position: relative; height: clamp(150px, 72vh, 1000px); background: transparent;
}
/* "You are here" — the camera centres on the player, so the marker sits dead centre. */
.mm-here {
  position: absolute; top: 50%; left: 50%; width: 10px; height: 10px;
  transform: translate(-50%, -50%); border-radius: 50%;
  background: #fff; box-shadow: 0 0 0 2px #11160f, 0 0 8px 3px rgba(255,255,255,.6);
  pointer-events: none;
}
.mm-here::after {
  content: ''; position: absolute; top: 50%; left: 50%; width: 22px; height: 22px;
  transform: translate(-50%, -50%); border-radius: 50%;
  border: 1px solid rgba(255,255,255,.5); animation: mm-ping 1.8s ease-out infinite;
}
@keyframes mm-ping { 0% { transform: translate(-50%,-50%) scale(.6); opacity: .9; } 100% { transform: translate(-50%,-50%) scale(1.6); opacity: 0; } }
.mm-info {
  position: absolute; left: 8px; bottom: 8px; pointer-events: none;
  padding: 3px 8px; border-radius: 6px; background: rgba(10,13,8,.7); color: #e8d9a0;
  font-size: clamp(12px, calc(3.2 * var(--u)), 22px);
}
/* Manual-mode walk-to marker (placed by tapping the minimap). */
.mm-x {
  position: absolute; transform: translate(-50%, -50%); pointer-events: none;
  color: #ff3b3b; font-weight: 900; line-height: 1;
  font-size: clamp(15px, calc(4 * var(--u)), 26px);
  text-shadow: 0 0 2px #000, 0 0 4px #000;
}
.mm-x.hidden { display: none; }

/* Static compass on the minimap (always north-up). Subtle edge letters that orient the
   east-west corridor without cluttering the terrain view. */
.mm-compass { position: absolute; inset: 0; pointer-events: none; }
.mm-c {
  position: absolute; color: #e8d9a0; opacity: .72; font-weight: 800;
  font-size: clamp(11px, calc(3 * var(--u)), 18px); line-height: 1;
  text-shadow: 0 0 2px #000, 0 0 4px #000;
}
.mm-c-n { top: 4px; left: 50%; transform: translateX(-50%); }
.mm-c-s { bottom: 4px; left: 50%; transform: translateX(-50%); }
.mm-c-w { left: 5px; top: 50%; transform: translateY(-50%); }
.mm-c-e { right: 5px; top: 50%; transform: translateY(-50%); color: #f0a24a; } /* east = tougher */

/* ---------------- Modals ---------------- */
.modal {
  position: absolute; inset: 0; z-index: 40;
  background: #000000aa; display: flex; align-items: center; justify-content: center;
  /* Keep the card (and its top-right ✕ close) clear of the phone status bar / notch and the
     home-indicator — a tall dialog is centred but can reach the screen edges, and the ✕ used
     to sit under the status bar and be hard to tap. */
  padding: calc(20px + env(safe-area-inset-top, 0px)) 20px calc(20px + env(safe-area-inset-bottom, 0px));
}
.modal-card {
  width: 100%; max-width: 380px;
  border: 1px solid #314733; border-radius: 16px; padding: 20px;
  /* Never taller than the (padded) viewport — scroll inside instead, so the
     bottom controls (e.g. Done) stay reachable on short landscape phones. */
  max-height: 100%; overflow-y: auto;
  /* Scroll cue: a soft shadow/fade shows at the top and/or bottom edge whenever
     there's more content to scroll to, and auto-hides once you reach an end. Pure
     CSS (Lea Verou's scroll-shadow trick): the two solid "cover" layers scroll
     WITH the content (background-attachment: local) and mask the fixed shadow
     layers except where content actually overflows. Makes it obvious the dialog
     scrolls on PC and unusual window sizes. rgba(27,42,31,*) == var(--panel). */
  background:
    linear-gradient(var(--panel) 30%, rgba(27, 42, 31, 0)) top / 100% 30px no-repeat,
    linear-gradient(rgba(27, 42, 31, 0), var(--panel) 70%) bottom / 100% 30px no-repeat,
    radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)) top / 100% 14px no-repeat,
    radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0)) bottom / 100% 14px no-repeat;
  background-color: var(--panel);
  background-attachment: local, local, scroll, scroll;
}

/* Interaction-spot readables (#interact-modal): parchment, deliberately unlike the
   brown menus — pale paper, dark ink, serif body — so notes/plaques/guestbooks feel
   like objects, not another game window. */
.modal-card.parchment {
  background: linear-gradient(160deg, #efe6cd, #e6d9b8 60%, #ddcfa9);
  border: 1px solid #b9a67c; border-radius: 8px;
  box-shadow: 0 8px 30px #000c, inset 0 0 60px #c9b88e55;
  color: #3a2f1c;
}
.modal-card.parchment h2 { color: #4a3a20; margin: 0 0 10px; font-size: 19px; }
.parchment-text {
  margin: 0; white-space: pre-wrap; line-height: 1.45; font-size: 14px;
  font-family: Georgia, 'Times New Roman', serif; color: #3a2f1c;
}
.parchment-reward { margin: 12px 0 0; font-weight: 600; color: #6a5115; font-size: 13px; }
.parchment-close {
  display: block; margin: 16px auto 0; min-width: 130px; padding: 10px 20px;
  border: 1px solid #a08a58; border-radius: 8px; cursor: pointer;
  background: linear-gradient(180deg, #d8c69a, #c4af7e); color: #3a2f1c; font-weight: 600;
}
.parchment-close:active { transform: translateY(1px); }

/* House cabinet storage (#storage-modal): a wider two-column dialog — cabinet | pack. */
#storage-modal .storage-card { max-width: 640px; }
.storage-sub { color: var(--muted); margin: 0 0 12px; font-size: 13px; }
.storage-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.storage-col .inv-list { max-height: 46vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
@media (max-width: 520px) { .storage-cols { grid-template-columns: 1fr; } .storage-col .inv-list { max-height: 30vh; } }
/* Stylized Close: a warm-wood pill that echoes the cabinet, with a subtle sheen + press feel. */
#storage-modal .build-actions { margin-top: 14px; }
#storage-close {
  min-width: 150px; padding: 11px 22px;
  border: 1px solid #6a5734; border-radius: 10px;
  background: linear-gradient(#3d2e1c, #2a2015);
  color: #f2dfad; font-weight: 800; letter-spacing: .4px; cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 236, 190, 0.08), 0 2px 6px rgba(0, 0, 0, 0.35);
  transition: background .12s ease, border-color .12s ease, transform .05s ease;
}
#storage-close:hover { background: linear-gradient(#4c3a24, #362817); border-color: #8a7038; color: #ffe9b8; }
#storage-close:active { transform: translateY(1px); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.4); }

/* Generic confirm dialog (#confirm-modal): message + Cancel/Confirm row. */
.confirm-msg { margin: 6px 0 16px; line-height: 1.4; }
.confirm-actions { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; }
.confirm-actions button { padding: 8px 16px; }

/* Account deletion (#delete-account-modal): a typed confirmation before an irreversible
   action, so it gets a real label + input rather than the plain confirm dialog. The delete
   button is red — this is the one destructive action in Settings. */
.del-acct-label { display: block; margin: 0 0 6px; font-size: .9em; opacity: .9; }
#del-acct-input { width: 100%; box-sizing: border-box; padding: 9px 10px; margin: 0 0 8px;
  border: 1px solid #6b5330; border-radius: 8px; background: #201a12; color: #f0e2c4;
  font: inherit; }
#del-acct-input:focus { outline: none; border-color: #a98544; box-shadow: 0 0 0 2px rgba(169, 133, 68, .3); }
#del-acct-err:empty { display: none; }
#del-acct-err { margin: 0 0 10px; font-size: .9em; color: #e08070; }
#del-acct-go { background: linear-gradient(#8c2f26, #6a201a); border-color: #b4574a; color: #ffe4de; }
#del-acct-go:hover:not(:disabled) { background: linear-gradient(#a3382d, #7d271f); }
#del-acct-go:disabled { opacity: .6; cursor: default; }

/* ☰ → Discord hub: Join / Link-account options + the persistent link-code panel. */
.discord-card p { margin: 0 0 12px; line-height: 1.4; }
.discord-card > button { display: block; width: 100%; margin: 0 0 10px; }
.discord-card #discord-close { background: none; border: 1px solid #314733; border-radius: 10px;
  color: var(--muted); font-weight: 600; padding: 10px; cursor: pointer; margin-top: 4px; }
.discord-link-panel { margin: 2px 0 10px; padding: 12px; border: 1px solid #314733; border-radius: 10px; background: #0e130d; }
.discord-link-panel.hidden { display: none; }
.discord-link-panel p { font-size: 14px; margin: 0 0 8px; }
.discord-muted { color: var(--muted); font-size: 13px; }
.discord-code { display: flex; align-items: center; gap: 10px; margin: 6px 0 10px; }
.discord-code span { flex: 1; font-family: ui-monospace, monospace; font-size: clamp(22px, 7vw, 30px);
  font-weight: 800; letter-spacing: .22em; color: var(--accent); background: #11160f;
  border: 1px solid #314733; border-radius: 8px; padding: 8px 12px; text-align: center; user-select: all; }
.discord-code button { border: 1px solid #314733; border-radius: 8px; background: var(--panel-2);
  color: var(--ink); font-weight: 700; padding: 8px 14px; cursor: pointer; white-space: nowrap; }
.discord-link-panel .danger { border: none; border-radius: 8px; background: #7a2a22; color: #ffecec;
  font-weight: 700; padding: 10px 16px; cursor: pointer; }

/* Visible, styled scrollbars on every scroll area, so it's obvious there's more
   to see on desktop (where the OS otherwise hides overlay scrollbars). */
.modal-card, .panel-body, .board-body, .lb-list, .tp-list, .profile-body, .chat-log {
  scrollbar-width: thin;                       /* Firefox */
  scrollbar-color: #4a6b46 #0e130d;            /* thumb, track (Firefox) */
}
.modal-card::-webkit-scrollbar, .panel-body::-webkit-scrollbar,
.board-body::-webkit-scrollbar, .lb-list::-webkit-scrollbar,
.tp-list::-webkit-scrollbar, .profile-body::-webkit-scrollbar,
.chat-log::-webkit-scrollbar { width: 12px; height: 12px; }
.modal-card::-webkit-scrollbar-track, .panel-body::-webkit-scrollbar-track,
.board-body::-webkit-scrollbar-track, .lb-list::-webkit-scrollbar-track,
.tp-list::-webkit-scrollbar-track, .profile-body::-webkit-scrollbar-track,
.chat-log::-webkit-scrollbar-track { background: #0e130d; border-radius: 8px; }
.modal-card::-webkit-scrollbar-thumb, .panel-body::-webkit-scrollbar-thumb,
.board-body::-webkit-scrollbar-thumb, .lb-list::-webkit-scrollbar-thumb,
.tp-list::-webkit-scrollbar-thumb, .profile-body::-webkit-scrollbar-thumb,
.chat-log::-webkit-scrollbar-thumb { background: #4a6b46; border-radius: 8px; border: 3px solid #0e130d; }
.modal-card::-webkit-scrollbar-thumb:hover, .panel-body::-webkit-scrollbar-thumb:hover,
.board-body::-webkit-scrollbar-thumb:hover, .lb-list::-webkit-scrollbar-thumb:hover,
.tp-list::-webkit-scrollbar-thumb:hover, .profile-body::-webkit-scrollbar-thumb:hover,
.chat-log::-webkit-scrollbar-thumb:hover { background: var(--accent-2); }
.modal-card h2 { margin: 0 0 10px; color: var(--accent); }
/* World overview map: a wide card with a horizontally-scrollable banner (the east-west corridor). */
.worldmap-card { max-width: min(96vw, 1100px); width: 96vw; }
.worldmap-where { margin: 0 0 8px; color: var(--accent-2); font-size: clamp(13px, calc(3.6 * var(--u)), 24px); }
/* Journey-progress bar: west (Rest) → east (frontier), with a marker at the player's position. */
.wm-journey { margin: 0 0 12px; }
.wm-journey.hidden { display: none; }
.wmj-track { position: relative; height: 10px; border-radius: 999px; border: 1px solid #314733;
  background: linear-gradient(90deg, #1c3a24 0%, #3a3414 55%, #4a1c18 100%); overflow: visible; }
.wmj-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 999px;
  background: linear-gradient(90deg, #5fd35f55, #f0d68a55); }
.wmj-marker { position: absolute; top: 50%; width: 14px; height: 14px; margin-left: -7px; z-index: 2;
  transform: translateY(-50%) rotate(45deg); background: #ffd23c; border: 1.5px solid #3a2a06;
  box-shadow: 0 0 8px rgba(255,210,60,.6); transition: left .3s ease; }
/* Town/region tick-marks along the journey track; the current town is brightened. */
.wmj-tick { position: absolute; top: -2px; bottom: -2px; width: 2px; margin-left: -1px; z-index: 1;
  background: #0c110b; opacity: .5; }
.wmj-tick.cur { width: 3px; opacity: 1; background: #fff0b0; box-shadow: 0 0 5px rgba(255,210,60,.7); }
.wmj-ends { display: flex; justify-content: space-between; margin-top: 5px;
  font-size: clamp(11px, calc(3 * var(--u)), 20px); color: var(--muted); }
.wmj-ends #wmj-pct { color: var(--accent); font-weight: 700; }
.worldmap-scroll { overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; border: 1px solid #314733; border-radius: 8px; background: #0e130d; }
.worldmap-img { display: block; height: clamp(200px, 44vh, 460px); width: auto; max-width: none; image-rendering: pixelated; }
/* The map ships as vertical slices laid side by side (a single 41752px-wide image decodes
   to ~190MB and was knocking over low-RAM phones). Slices carry width/height attributes so
   layout reserves their space before load, and lazy-loading only decodes what's scrolled to. */
.worldmap-strip { display: flex; width: max-content; }
.worldmap-strip img { display: block; flex: 0 0 auto; height: clamp(200px, 44vh, 460px); width: auto; max-width: none; image-rendering: pixelated; }
.worldmap-hint { margin: 8px 0 14px; font-size: clamp(12px, calc(3.2 * var(--u)), 22px); }
.worldmap-status { margin: 0 0 8px; font-size: clamp(13px, calc(3.6 * var(--u)), 24px); }

/* World-map settlements list: town name + map coordinates + level band. Two responsive columns
   on wide screens so a long corridor of towns stays scannable. */
.wm-towns { margin: 0 0 12px; }
.wm-towns.hidden { display: none; }
.wm-towns-h { margin: 0 0 8px; font-size: clamp(13px, calc(3.5 * var(--u)), 22px); color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; font-weight: 700; }
.wm-town { display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 10px;
  padding: clamp(6px, calc(1.6 * var(--u)), 10px) 2px; border-bottom: 1px solid #1e2c20; }
.wm-town.cur { background: #17251b; border-radius: 8px; padding-left: 8px; padding-right: 8px; border-bottom-color: transparent; }
.wm-town-name { color: var(--ink); font-weight: 600; font-size: clamp(13px, calc(3.6 * var(--u)), 22px);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wm-town.cur .wm-town-name { color: var(--accent); }
.wm-town-coord { color: var(--accent-2); font-variant-numeric: tabular-nums; font-size: clamp(12px, calc(3.2 * var(--u)), 20px); white-space: nowrap; }
@media (min-width: 640px) { .wm-towns { display: grid; grid-template-columns: 1fr 1fr; gap: 0 22px; }
  .wm-towns-h { grid-column: 1 / -1; } }
.modal-card h3 { margin: 16px 0 8px; font-size: 14px; color: var(--muted); }

/* Death / respawn celebration */
#death-modal { background: #06080af2; } /* near-opaque: hides the respawn teleport behind it */
.death-card { text-align: center; max-width: 600px; border-color: #6b5a2a; padding: clamp(24px, calc(6 * var(--u)), 48px); }
.death-trophy { font-size: clamp(48px, calc(13 * var(--u)), 96px); line-height: 1; margin-bottom: 4px; }
.death-card h2 { color: #f2d23a; font-size: clamp(28px, calc(7.4 * var(--u)), 52px); }
.death-body { margin: 14px 0 4px; }
.death-line { font-size: clamp(18px, calc(5 * var(--u)), 34px); padding: 8px 0; }
.death-line b { color: #f2d23a; font-size: clamp(22px, calc(6 * var(--u)), 42px); }
/* New personal-best distance: a warm gold celebration with a soft glow + pop-in. */
.death-record { color: #f2d23a; font-weight: 700; text-shadow: 0 0 14px rgba(242,210,58,.45); animation: death-record-pop .5s ease-out; }
.death-record b { color: #fff0b0; }
@keyframes death-record-pop { 0% { transform: scale(.7); opacity: 0; } 60% { transform: scale(1.08); opacity: 1; } 100% { transform: scale(1); } }
.death-count { margin-top: 16px; font-size: clamp(14px, calc(3.8 * var(--u)), 26px); color: var(--muted); }
.death-count span { color: var(--accent); font-weight: 700; }
/* Reassurance line: death keeps your gear/coins — a calm, non-punishing note. */
.death-reassure { margin-top: 10px; font-size: clamp(14px, calc(3.8 * var(--u)), 26px); color: #7fd6a0; }
.summary-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #223528; font-size: 14px; }
.summary-row span:last-child { color: var(--accent-2); font-weight: 600; }
/* "Best find" banner in the offline summary — left border tinted by rarity (via .rar-*). */
.summary-bestitem { border-left: 4px solid #4ade80; background: #14241acc; border-radius: 8px; padding: 8px 12px; margin: 0 0 8px; font-weight: 700; color: #e8d9a0; }
#offline-modal .summary-bestitem { font-size: clamp(18px, calc(4.8 * var(--u)), 34px); padding: clamp(10px, calc(2.4 * var(--u)), 18px) clamp(12px, calc(3 * var(--u)), 22px); }
/* New furthest-east record reached while away — a warm gold celebration (matches the death screen). */
.summary-record { border-left: 4px solid #f2d23a; background: #2c2410cc; border-radius: 8px; padding: 8px 12px; margin: 0 0 8px; font-weight: 800; color: #f2d23a; text-shadow: 0 0 12px rgba(242,210,58,.4); }
#offline-modal .summary-record { font-size: clamp(18px, calc(4.8 * var(--u)), 34px); padding: clamp(10px, calc(2.4 * var(--u)), 18px) clamp(12px, calc(3 * var(--u)), 22px); }

/* Offline level-ups: a celebratory banner above the summary rows. */
.summary-levelup {
  text-align: center; font-weight: 800; color: #1a1306;
  background: linear-gradient(#ffe08a, #e8b94a); border: 1px solid #b8862f; border-radius: 8px;
  padding: 8px 12px; margin: 0 0 8px;
}
.summary-levelup.sub { background: none; border: none; color: var(--accent); font-weight: 700; padding: 2px 0; margin: 0 0 4px; }
#offline-modal .summary-levelup { font-size: clamp(20px, calc(5.6 * var(--u)), 40px); padding: clamp(10px, calc(2.4 * var(--u)), 18px); }
#offline-modal .summary-levelup.sub { font-size: clamp(17px, calc(4.6 * var(--u)), 32px); }

/* Offline cap nudge: shown under the summary rows when travel hit the player's cap. */
.summary-note { border-left: 4px solid var(--accent); background: #1a1f2acc; border-radius: 8px; padding: 8px 12px; margin: 8px 0 0; color: #cdd6e4; }
.summary-note b { color: var(--accent); }
#offline-modal .summary-note { font-size: clamp(15px, calc(4 * var(--u)), 28px); padding: clamp(10px, calc(2.4 * var(--u)), 18px); }

/* "While you were away" — roughly double-size, scales with screen width */
#offline-modal .modal-card { max-width: 640px; padding: clamp(28px, calc(7 * var(--u)), 56px); }
#offline-modal .modal-card h2 { font-size: clamp(30px, calc(8 * var(--u)), 56px); margin: 0 0 18px; }
#offline-modal .summary-row { padding: clamp(11px, calc(2.6 * var(--u)), 20px) 0; font-size: clamp(20px, calc(5.4 * var(--u)), 38px); }
#offline-modal #offline-close { font-size: clamp(20px, calc(5.4 * var(--u)), 38px); padding: clamp(16px, calc(3.6 * var(--u)), 28px); margin-top: 24px; }
/* Admin teleport menu */
/* Live coordinate readout (moved off the main HUD into the admin panel). */
.tp-here { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; color: var(--muted); font-size: clamp(14px, calc(3.6 * var(--u)), 24px); }
.tp-list { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; max-height: 46vh; overflow-y: auto; margin-bottom: 10px; }
.tp-btn {
  padding: 12px 11px; border-radius: 8px; border: 1px solid #314733; background: #0e130d;
  color: var(--ink); font-size: clamp(15px, calc(4 * var(--u)), 26px); cursor: pointer; text-align: left;
}
.tp-btn:active { background: var(--panel-2); }
.tp-btn small { color: var(--muted); font-size: clamp(13px, calc(3.4 * var(--u)), 22px); }
/* Destination blurb under the name (teleport-scroll picker) — quieter than the label,
   and allowed to wrap, since the list is a two-column grid on narrow phones. */
.tp-btn small.tp-desc { display: block; margin-top: 3px; line-height: 1.3; font-style: italic; }
.tp-xy { display: flex; gap: 6px; align-items: center; margin-bottom: 10px; }
.tp-xy input { width: clamp(70px, calc(20 * var(--u)), 120px); height: clamp(42px, calc(11 * var(--u)), 62px); padding: 0 10px; border-radius: 8px; border: 1px solid #314733; background: #0e130d; color: var(--ink); font-size: clamp(15px, calc(4 * var(--u)), 26px); }
.tp-xy button { height: clamp(42px, calc(11 * var(--u)), 62px); padding: 0 clamp(16px, calc(4.2 * var(--u)), 30px); border-radius: 8px; border: none; background: var(--accent-2); color: #11160f; font-weight: 700; cursor: pointer; font-size: clamp(15px, calc(4 * var(--u)), 26px); }
.tp-player { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.tp-player label { color: var(--muted, #9bb09b); font-size: clamp(14px, calc(3.6 * var(--u)), 22px); width: 100%; }
.tp-player select { flex: 1 1 auto; min-width: 0; height: clamp(42px, calc(11 * var(--u)), 62px); padding: 0 8px; border-radius: 8px; border: 1px solid #314733; background: #0e130d; color: var(--ink); font-size: clamp(14px, calc(3.6 * var(--u)), 22px); }
.tp-player button { height: clamp(42px, calc(11 * var(--u)), 62px); padding: 0 clamp(16px, calc(4.2 * var(--u)), 30px); border-radius: 8px; border: none; background: var(--accent-2); color: #11160f; font-weight: 700; cursor: pointer; font-size: clamp(15px, calc(4 * var(--u)), 26px); }

/* Who's online roster (tap the green HUD badge) */
.online-count { color: var(--accent-2); font-weight: 700; }
.online-list { display: flex; flex-direction: column; gap: 5px; max-height: 56vh; overflow-y: auto; margin-bottom: 12px; }
.online-empty { color: var(--muted); text-align: center; padding: 18px 0; font-size: clamp(14px, calc(3.6 * var(--u)), 22px); }
.online-row {
  display: flex; align-items: center; gap: 8px; padding: 10px 11px; border-radius: 8px;
  border: 1px solid #314733; background: #0e130d; font-size: clamp(15px, calc(4 * var(--u)), 26px);
}
.online-row.is-self { border-color: var(--accent-2); background: #12190e; }
.online-lvl { flex: 0 0 auto; min-width: 3.2em; color: var(--muted); font-size: clamp(12px, calc(3.2 * var(--u)), 20px); }
.online-name {
  flex: 1 1 auto; min-width: 0; color: var(--ink); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: left; background: none; border: none; padding: 0; margin: 0;
  font: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.online-name:hover { color: var(--accent-2); text-decoration: underline; }
.online-name:active { opacity: 0.7; }
.online-you { color: var(--accent-2); font-weight: 700; }
.online-region { flex: 0 0 auto; color: var(--muted); font-size: clamp(12px, calc(3.2 * var(--u)), 20px); text-align: right; }
.tp-debug { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; padding-top: 10px; border-top: 1px solid #243024; }
.tp-debug label { color: var(--muted, #9bb09b); font-size: clamp(14px, calc(3.6 * var(--u)), 22px); }
.tp-debug select { height: clamp(38px, calc(10 * var(--u)), 56px); padding: 0 8px; border-radius: 8px; border: 1px solid #314733; background: #0e130d; color: var(--ink); font-size: clamp(14px, calc(3.6 * var(--u)), 22px); }
.tp-debug .dbg-toggle { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.tp-debug .dbg-toggle input { width: 18px; height: 18px; }

/* Leaderboards ("Hall of Wanderers") */
#board-modal .modal-card { max-width: 640px; padding: clamp(24px, calc(6 * var(--u)), 48px); display: flex; flex-direction: column; }
#board-modal .modal-card h2 { font-size: clamp(26px, calc(7 * var(--u)), 50px); margin: 0 0 16px; }
#board-modal #board-close { font-size: clamp(18px, calc(4.8 * var(--u)), 32px); padding: clamp(14px, calc(3.4 * var(--u)), 24px); margin-top: 18px; width: 100%; }
/* Category chips: a single horizontally-scrolling row (no vertical sprawl on mobile) */
.board-tabs {
  display: flex; flex-wrap: nowrap; gap: 6px; margin-bottom: 10px;
  overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 4px; scrollbar-width: thin;
}
.board-tab {
  padding: 9px 14px; border-radius: 999px; border: 1px solid #314733; white-space: nowrap; flex: 0 0 auto;
  background: #0e130d; color: var(--muted); font-size: clamp(14px, calc(3.7 * var(--u)), 24px); cursor: pointer;
}
.board-tab.active { border-color: var(--accent); background: var(--panel-2); color: var(--ink); }
/* The list is the only scroll region: header (title + tabs) and the Close button
   stay pinned so the modal fits any height, incl. short landscape phones. */
.board-body { min-height: 0; flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; }
#board-modal .board-tabs, #board-modal #board-close { flex: 0 0 auto; }

.lb-tip { color: var(--muted); font-size: clamp(12px, calc(3.1 * var(--u)), 20px); line-height: 1.35; margin: 0 2px 10px; }
.lb-periods { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 10px; }
.lb-chip {
  padding: 6px 12px; border-radius: 999px; border: 1px solid #314733;
  background: #0e130d; color: var(--muted); font-size: clamp(13px, calc(3.4 * var(--u)), 22px); cursor: pointer;
}
.lb-chip.active { border-color: var(--accent-2); background: var(--panel-2); color: var(--ink); }
.lb-reset { color: var(--muted); font-size: clamp(11px, calc(2.9 * var(--u)), 18px); margin-left: auto; white-space: nowrap; }
.lb-search { margin-bottom: 8px; }
.lb-search input {
  width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 10px;
  border: 1px solid #314733; background: #0e130d; color: var(--ink);
  font-size: clamp(14px, calc(3.7 * var(--u)), 24px);
}
.lb-list { min-height: 120px; max-height: 42vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }

.lb-spinner { display: flex; justify-content: center; padding: 28px 0; }
.lb-spinner span {
  width: 28px; height: 28px; border-radius: 50%;
  border: 3px solid #314733; border-top-color: var(--accent); animation: lb-spin 0.8s linear infinite;
}
@keyframes lb-spin { to { transform: rotate(360deg); } }

/* Podium for the top three */
.lb-podium { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: 6px; align-items: end; margin: 4px 0 12px; }
.lb-pod {
  display: flex; flex-direction: column; align-items: center; gap: 4px; cursor: pointer;
  background: var(--panel-2); border: 1px solid #314733; border-radius: 12px; padding: 12px 6px 10px;
}
.lb-pod.p1 { border-color: var(--accent); background: #2c2716; order: 2; padding-top: 18px; }
.lb-pod.p2 { order: 1; }
.lb-pod.p3 { order: 3; }
.lb-pod.me { box-shadow: inset 0 0 0 2px var(--accent-2); }
.lb-pod-medal { font-size: clamp(22px, calc(6 * var(--u)), 40px); line-height: 1; }
.lb-pod-name { font-weight: 700; font-size: clamp(13px, calc(3.4 * var(--u)), 22px); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-pod-val { color: var(--accent-2); font-weight: 700; font-size: clamp(13px, calc(3.4 * var(--u)), 22px); text-align: center; }
.lb-pod-val small { color: var(--muted); font-weight: 400; }
.lb-pod .br-av { width: clamp(38px, calc(9.5 * var(--u)), 64px); height: clamp(38px, calc(9.5 * var(--u)), 64px); font-size: clamp(18px, calc(4.8 * var(--u)), 32px); }

/* List rows */
.lb-row, .lb-you {
  display: flex; align-items: center; gap: 10px; padding: 10px 6px;
  border-bottom: 1px solid #223528; font-size: clamp(15px, calc(4.2 * var(--u)), 28px); cursor: pointer;
}
.lb-row.me { background: #2a3a2a; border-radius: 8px; }
.br-av {
  width: clamp(30px, calc(7.5 * var(--u)), 50px); height: clamp(30px, calc(7.5 * var(--u)), 50px);
  border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  color: #fff; font-weight: 700; font-size: clamp(14px, calc(3.8 * var(--u)), 24px); text-shadow: 0 1px 2px #0008;
}
.br-rank { min-width: clamp(34px, calc(8.5 * var(--u)), 56px); text-align: center; font-weight: 700; color: var(--accent); flex: 0 0 auto; }
.br-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; flex-direction: column; }
.br-name small { color: var(--muted); font-weight: 400; font-size: 0.72em; }
.br-val { color: var(--accent-2); font-weight: 700; white-space: nowrap; flex: 0 0 auto; }
.br-val small { color: var(--muted); font-weight: 400; }
.br-go { color: var(--muted); font-size: 1.2em; flex: 0 0 auto; }

.lb-more {
  width: 100%; margin-top: 10px; padding: 10px; border-radius: 10px; cursor: pointer;
  border: 1px solid #314733; background: #0e130d; color: var(--ink); font-size: clamp(14px, calc(3.7 * var(--u)), 24px);
}
.lb-you {
  margin-top: 8px; border: 1px solid var(--accent); border-radius: 10px; background: #2a3a2a; cursor: default;
}
.lb-you-label { color: var(--accent); font-weight: 700; font-size: 0.8em; flex: 0 0 auto; }

/* Player profile */
#profile-modal .modal-card { max-width: 560px; padding: clamp(20px, calc(5 * var(--u)), 40px); }
.profile-body { max-height: 64vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.profile-body h3 { margin: 16px 0 4px; font-size: clamp(13px, calc(3.4 * var(--u)), 22px); color: var(--accent); border-bottom: 1px solid #243024; padding-bottom: 4px; }
.profile-body .summary-row { font-size: clamp(14px, calc(3.7 * var(--u)), 24px); }
.profile-body .summary-row small { color: var(--muted); }
.pf-head { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.pf-head .br-av { width: clamp(48px, calc(12 * var(--u)), 80px); height: clamp(48px, calc(12 * var(--u)), 80px); font-size: clamp(22px, calc(6 * var(--u)), 40px); }
.pf-name { font-weight: 700; font-size: clamp(20px, calc(5.4 * var(--u)), 38px); color: var(--accent); }
.pf-sub { color: var(--muted); font-size: clamp(12px, calc(3.1 * var(--u)), 20px); }
.pf-badges, .pf-titles { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.pf-badge, .pf-title {
  font-size: clamp(12px, calc(3.1 * var(--u)), 20px); padding: 4px 10px; border-radius: 999px;
  border: 1px solid #314733; background: #0e130d; color: var(--ink);
}
.pf-badge.gold { border-color: var(--accent); background: #2c2716; color: #f2d23a; }
.pf-badge.silver { border-color: #9aa6b0; color: #d8dde2; }
.pf-badge.bronze { border-color: #b07a45; color: #e3b489; }
.pf-title { border-color: var(--accent-2); color: var(--accent-2); }
.pf-privacy {
  display: flex; align-items: center; gap: 8px; margin-top: 16px; padding-top: 12px;
  border-top: 1px solid #243024; color: var(--muted); font-size: clamp(13px, calc(3.4 * var(--u)), 22px); cursor: pointer;
}
.pf-privacy input { width: 18px; height: 18px; }
#profile-modal #profile-close { width: 100%; margin-top: 16px; font-size: clamp(16px, calc(4.2 * var(--u)), 28px); padding: clamp(12px, calc(3 * var(--u)), 22px); }

.mode-list { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.mode-opt {
  padding: 12px 10px; border-radius: 10px; border: 1px solid #314733;
  background: #0e130d; color: var(--ink); cursor: pointer; text-align: left;
}
.mode-opt small { display: block; color: var(--muted); font-size: 14px; margin-top: 3px; }
/* The "resources needed" line under a recipe in the mode pickers — a warm ingredient tint so it
   reads apart from the grey level/XP line above it. */
.mode-opt small.picker-res { color: #c9a06a; margin-top: 1px; }
.mode-opt.active { border-color: var(--accent); background: var(--panel-2); }
.mode-opt.locked, .mode-opt:disabled {
  opacity: .55; cursor: not-allowed; border-color: #2b322b; background: #0b0e0b;
}
.match-offline {
  display: flex; align-items: center; gap: 8px; margin-top: 14px;
  font-size: 14px; color: var(--ink); cursor: pointer;
}
.match-offline input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
#mode-done, #offline-close, #picker-back { margin-top: 14px; width: 100%; }

/* ---------------- Skills extras ---------------- */
.skill-sub { font-size: clamp(14px, calc(3.8 * var(--u)), 26px); color: var(--muted); margin: 3px 0 10px; }
.focus-h { margin: 6px 0 8px; font-size: clamp(16px, calc(4.2 * var(--u)), 28px); color: var(--muted); }
.focus-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.focus-btn {
  padding: 14px; border-radius: 10px; border: 1px solid #314733;
  background: #0e130d; color: var(--ink); cursor: pointer; font-size: clamp(16px, calc(4.4 * var(--u)), 30px);
}
.focus-btn.active { border-color: var(--accent); background: var(--panel-2); color: var(--accent); }
.focus-desc { margin: 10px 0 2px; font-size: clamp(14px, calc(3.8 * var(--u)), 26px); color: var(--muted); line-height: 1.35; }

/* Battle panel removed — combat is now choreographed on the map (see combat-fx.js);
   the result is surfaced via toast notifications. */

/* ---------------- Inventory ---------------- */
/* Sell All + sort sit together at the right; the pack count now lives in the panel title. */
.inv-head { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-bottom: 10px; }
.inv-full-hint { color: #f0c0b6; background: #3a1c1ccc; border: 1px solid #5a2c25; border-radius: 8px;
  padding: clamp(7px, calc(1.8 * var(--u)), 14px) clamp(10px, calc(2.6 * var(--u)), 18px); margin: 0 0 10px;
  font-size: clamp(14px, calc(3.7 * var(--u)), 24px); }
.inv-sellall {
  font-size: clamp(15px, calc(4 * var(--u)), 28px); padding: 10px 14px; border-radius: 8px;
  border: 1px solid #5a3a3a; background: #2a1717; color: #ffb9a8; cursor: pointer; white-space: nowrap;
}
.inv-sellall:active { background: #3a1f1f; }
.inv-sellall:disabled { opacity: 0.45; cursor: default; }
#inv-sort { background: #0e130d; color: var(--ink); border: 1px solid #314733; border-radius: 8px; padding: 10px 12px; font-size: clamp(15px, calc(4 * var(--u)), 28px); }
/* PACK DENSITY. A pack row is built to occupy the same vertical space as a Skills row
   (.sk-row): one line of name + actions, one line of detail, an icon the size of the skill
   badge. Every size below is deliberately the same clamp() the Skills screen uses, so the two
   lists read as one family — change them together. */
.inv-list { display: flex; flex-direction: column; gap: 4px; }
.inv-item {
  border: 1px solid #314733; border-left-width: 4px; border-radius: 9px;
  background: #0e130d; padding: clamp(5px,calc(1.4 * var(--u)),9px) clamp(7px,calc(1.9 * var(--u)),12px);
  display: flex; flex-direction: row; align-items: center; gap: 9px;
}
/* Standalone item artwork, to the left of the name/stats/buttons. Pixel art, so
   keep it crisp at any size. Sits on a faint inset tile so it reads as an item.
   Sized to .sk-badge — the skill monogram it sits level with. */
.ii-icon {
  flex-shrink: 0; width: clamp(30px, calc(8.2 * var(--u)), 44px); height: clamp(30px, calc(8.2 * var(--u)), 44px);
  image-rendering: pixelated; border-radius: 8px;
  background: #161d14; border: 1px solid #243024; padding: 2px;
}
.ii-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* Locked or too-low-to-equip: dim the icon + text as a cue, but NEVER the action buttons —
   Lock/Sell stay fully solid and usable. (A whole-row opacity would dim the buttons too,
   and child opacity can't undo a faded parent.) */
.inv-item.locked .ii-icon, .inv-item.locked .ii-main { opacity: 0.85; }
.inv-item.under-req .ii-icon, .inv-item.under-req .ii-main { opacity: 0.7; }
.ii-sub .req-unmet { color: #fbbf24; }
/* Name line: name (and stack count) on the left, the action buttons pushed hard right —
   mirrors .sk-top, which does the same with the skill name and its level. */
.ii-main { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* The name still wraps rather than truncating, so a long name is never hidden; at this size
   almost everything fits one line, and the buttons hold their width either way. */
.ii-name { flex: 1; min-width: 0; font-weight: 700;
  font-size: clamp(14px, calc(3.9 * var(--u)), 24px); white-space: normal; overflow-wrap: anywhere; }
.ii-sub { font-size: clamp(11px, calc(3 * var(--u)), 17px); color: var(--muted); margin-top: 0; }
/* Perishable countdown (raw fish). Muted while there's time, amber in the last hour. */
.ii-spoil { color: #9aa88f; }
.ii-spoil.soon { color: #e0a86a; font-weight: 600; }
.ii-sub .stat-better { color: #4ade80; }
.ii-sub .stat-worse { color: #f87171; }
.ii-up { display: inline-block; margin-left: 5px; padding: 0 5px; border-radius: 5px; vertical-align: middle;
  font-size: clamp(9px, calc(2.4 * var(--u)), 14px); font-weight: 700; color: #0e2a16;
  background: #4ade80; letter-spacing: 0.02em; white-space: nowrap; }
/* Compact inline actions. They hug their labels (no flex:1 stretching them across the row)
   and never shrink below the text, so Equip/Lock/Sell stay readable and tappable while taking
   a fraction of the room the old stacked button bar did. */
.ii-actions { display: flex; gap: 4px; flex-shrink: 0; }
.ii-actions button {
  flex: 0 0 auto;
  font-size: clamp(11px, calc(2.9 * var(--u)), 16px);
  padding: clamp(3px,calc(1 * var(--u)),6px) clamp(6px,calc(1.7 * var(--u)),11px);
  min-height: clamp(22px, calc(6 * var(--u)), 30px);
  border-radius: 6px; white-space: nowrap; line-height: 1.15;
  border: 1px solid #314733; background: var(--panel-2); color: var(--ink); cursor: pointer;
}
.ii-actions button:active { background: #2c4233; }
.ii-actions button[disabled] { opacity: .45; }

/* Consumable stacks (Teleport Scroll). A distinct gold accent; the icon box centres the glyph,
   and the quantity badge mirrors the in-game "x37" stack count. */
.inv-consumables { margin-bottom: 8px; }
.inv-item.inv-consumable { border-left-color: #d9b24a; }
.inv-item.inv-consumable .ii-name { color: #f0d68a; }
.ic-icon { display: flex; align-items: center; justify-content: center; font-size: clamp(20px, calc(5.6 * var(--u)), 32px);
  flex-shrink: 0; width: clamp(30px, calc(8.2 * var(--u)), 44px); height: clamp(30px, calc(8.2 * var(--u)), 44px); }
.ic-qty { display: inline-block; margin-left: 4px; padding: 0 6px; border-radius: 5px; vertical-align: middle;
  font-size: clamp(11px, calc(2.9 * var(--u)), 17px); font-weight: 700; color: #2a230a; background: #d9b24a; }

/* Teleport picker: subtitle + the "you are here" current-location row. */
.tp-sub { color: var(--muted); margin: 0 0 8px; font-size: clamp(14px, calc(3.8 * var(--u)), 26px); }
.tp-btn.tp-here { opacity: 0.6; }

/* rarity accents (left border + name colour) */
.rar-worn { border-left-color: #7e8a78; } .rar-worn .ii-name, .es-item.rar-worn { color: #b7c0b0; }
.rar-standard { border-left-color: #c9c9c9; } .rar-standard .ii-name, .es-item.rar-standard { color: #e6e6e6; }
.rar-refined { border-left-color: #6fae6a; } .rar-refined .ii-name, .es-item.rar-refined { color: #93d68c; }
.rar-rare { border-left-color: #5a86c0; } .rar-rare .ii-name, .es-item.rar-rare { color: #8db4ec; }
.rar-epic { border-left-color: #b46fd0; } .rar-epic .ii-name, .es-item.rar-epic { color: #d29cef; }
.rar-relic { border-left-color: #d8a24a; } .rar-relic .ii-name, .es-item.rar-relic { color: #f0c878; }

/* ---------------- Equipment (character panel) ---------------- */
.eq-h { margin: 11px 0 5px; font-size: clamp(12px, calc(3.2 * var(--u)), 20px); color: var(--muted); }
.equip-grid { display: flex; flex-direction: column; gap: 2px; }
.equip-slot { display: flex; align-items: center; gap: 8px; padding: clamp(6px,calc(1.7 * var(--u)),10px) 0; border-bottom: 1px solid #223528; }
.es-icon { flex-shrink: 0; width: clamp(30px, calc(8.2 * var(--u)), 44px); height: clamp(30px, calc(8.2 * var(--u)), 44px); image-rendering: pixelated; border-radius: 6px; background: #161d14; border: 1px solid #243024; }
.es-icon-empty { background: #11160f; border-style: dashed; }
/* Equipped-gear list on another player's profile card. */
.pf-equip { display: flex; flex-direction: column; gap: 4px; margin-bottom: 6px; }
.pf-eq-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-bottom: 1px solid #223528; }
.pf-eq-main { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.pf-eq-name { font-weight: 600; color: #e6e6e6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: clamp(14px, calc(3.9 * var(--u)), 24px); }
.pf-eq-slot { color: var(--muted); font-size: clamp(11px, calc(3 * var(--u)), 17px); }
.pf-eq-name.rar-worn { color: #b7c0b0; } .pf-eq-name.rar-standard { color: #e6e6e6; }
.pf-eq-name.rar-refined { color: #93d68c; } .pf-eq-name.rar-rare { color: #8db4ec; }
.pf-eq-name.rar-epic { color: #d29cef; } .pf-eq-name.rar-relic { color: #f0c878; }
.es-label { width: clamp(62px, calc(16 * var(--u)), 104px); color: var(--muted); font-size: clamp(11px, calc(3 * var(--u)), 17px); flex-shrink: 0; }
.es-item { flex: 1; font-size: clamp(14px, calc(3.9 * var(--u)), 24px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.es-item.empty { color: #5c6a54; }
/* Name + per-item stats stacked in an equipped slot (so you can see what each worn piece gives). */
.es-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.es-main .es-item { flex: 0 0 auto; }
.es-stats { font-size: clamp(11px, calc(3 * var(--u)), 17px); color: var(--muted); line-height: 1.3; }
.es-remove { font-size: clamp(11px, calc(2.9 * var(--u)), 16px);
  padding: clamp(3px,calc(1 * var(--u)),6px) clamp(6px,calc(1.7 * var(--u)),11px);
  min-height: clamp(22px, calc(6 * var(--u)), 30px); border-radius: 6px; white-space: nowrap; border: 1px solid #314733; background: var(--panel-2); color: var(--ink); cursor: pointer; }
.set-bonuses { margin-top: 8px; font-size: clamp(15px, calc(4 * var(--u)), 26px); color: var(--accent); }
.set-bonuses > div { padding: 2px 0; }
.set-prog { display: flex; justify-content: space-between; gap: 10px; padding: 4px 0; font-size: clamp(14px, calc(3.8 * var(--u)), 24px); color: var(--muted); }
.set-prog span:last-child { color: var(--accent-2); white-space: nowrap; }
.set-prog small { opacity: .7; }

/* ---------------- Shop ---------------- */
.shop-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.shop-name { font-weight: 700; color: var(--accent); }
.shop-name { font-size: clamp(14px, calc(3.9 * var(--u)), 24px); }
.shop-coins { color: #f0c878; font-size: clamp(12px, calc(3.2 * var(--u)), 20px); }
.shop-hint { background: #2a210f; border: 1px solid #5a4a2a; color: #e6d4a0; border-radius: 8px; padding: 8px 11px; font-size: clamp(11px, calc(3 * var(--u)), 17px); margin-bottom: 6px; }
.shop-h { margin: 10px 0 5px; font-size: clamp(12px, calc(3.2 * var(--u)), 20px); color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.shop-note { color: var(--muted); border: 1px dashed #3a4a2c; border-radius: 8px; padding: 7px 10px; margin: 4px 0 6px;
  font-size: clamp(11px, calc(3 * var(--u)), 17px); line-height: 1.35; }
.shop-item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  border: 1px solid #314733; border-left-width: 4px; border-radius: 10px;
  background: #0e130d; padding: clamp(5px,calc(1.4 * var(--u)),9px) clamp(7px,calc(1.9 * var(--u)),12px); margin-bottom: 4px;
}
.shop-item.service { border-left-color: #6fae6a; }
.si-icon { flex-shrink: 0; width: clamp(30px, calc(8.2 * var(--u)), 44px); height: clamp(30px, calc(8.2 * var(--u)), 44px); image-rendering: pixelated; border-radius: 7px; background: #161d14; border: 1px solid #243024; padding: 2px; }
.si-main { flex: 1; min-width: 0; }
/* The shop name no longer truncates at this size — most fit, and a long one wraps rather than
   ending in an ellipsis the player can't expand. */
.si-name { font-weight: 700; font-size: clamp(14px, calc(3.9 * var(--u)), 24px); white-space: normal; overflow-wrap: anywhere; }
.si-sub { font-size: clamp(11px, calc(3 * var(--u)), 17px); color: var(--muted); margin-top: 1px; }
.si-req { margin-top: 2px; color: #f87171; font-size: clamp(10px, calc(2.7 * var(--u)), 15px); font-weight: 600; }
.si-sub .stat-better { color: #4ade80; }
.si-sub .stat-worse { color: #f87171; }
.si-up { display: inline-block; padding: 0 5px; border-radius: 6px; font-weight: 700; color: #0e2a16; background: #4ade80; }
.buy-btn, .sell-btn {
  flex-shrink: 0; font-size: clamp(12px, calc(3.2 * var(--u)), 18px);
  padding: clamp(5px,calc(1.4 * var(--u)),9px) clamp(9px,calc(2.4 * var(--u)),15px);
  min-height: clamp(26px, calc(7 * var(--u)), 34px); border-radius: 7px; cursor: pointer; white-space: nowrap;
  border: 1px solid #6a4f1c; background: linear-gradient(#d8b25a, #b8862f); color: #1a1306; font-weight: 700;
}
.sell-btn { border-color: #314733; background: var(--panel-2); color: var(--ink); font-weight: 600; }
.buy-btn:disabled, .sell-btn:disabled { opacity: 0.4; cursor: not-allowed; filter: grayscale(0.5); }
/* Buy button + its (touch-visible) coin-shortfall note, stacked as one non-shrinking cell. */
.buy-wrap { flex-shrink: 0; display: flex; flex-direction: column; align-items: stretch; gap: 3px; }
.buy-need { font-size: clamp(9px, calc(2.5 * var(--u)), 14px); color: #e0a86a; text-align: center; line-height: 1.1; }

/* ---------------- Random-event modal ---------------- */
#event-modal .modal-card h2 { font-size: clamp(26px, calc(7 * var(--u)), 50px); }
.event-badge {
  display: inline-block; font-size: clamp(13px, calc(3.6 * var(--u)), 24px); text-transform: uppercase; letter-spacing: 0.5px;
  padding: 5px 12px; border-radius: 999px; margin-bottom: 10px;
  border: 1px solid #314733; background: #0e130d; color: var(--muted);
}
.event-badge.rar-common { color: #c9c9c9; }
.event-badge.rar-uncommon { color: #93d68c; border-color: #3f6e3a; }
.event-badge.rar-rare { color: #8db4ec; border-color: #3a5680; }
.event-badge.rar-epic { color: #d29cef; border-color: #6a3a80; }
.event-badge.rar-legendary { color: #f0c878; border-color: #7a5a1c; box-shadow: 0 0 10px #7a5a1c66; }
#event-text { margin: 4px 0 16px; line-height: 1.4; font-size: clamp(18px, calc(4.8 * var(--u)), 34px); }
.event-options { display: flex; flex-direction: column; gap: 8px; }
.event-opt {
  padding: 15px 16px; border-radius: 10px; border: 1px solid #314733;
  background: #0e130d; color: var(--ink); font-size: clamp(17px, calc(4.6 * var(--u)), 32px); cursor: pointer; text-align: left;
}
.event-opt:active { background: var(--panel-2); }
/* Choice-event countdown: a depleting bar showing the auto-resolve timer (server picks the safe
   default at expiry). The fill width is transitioned from JS over the server-sent timeoutMs. */
.event-timer { margin-top: 12px; height: 6px; border-radius: 3px; background: #0e130d; border: 1px solid #314733; overflow: hidden; }
.event-timer.hidden, .event-timer-note.hidden { display: none; }
.event-timer-fill { height: 100%; width: 100%; background: linear-gradient(90deg, #e8c45a, #c8922f); }
.event-timer-note { margin: 6px 2px 0; color: var(--muted); font-size: clamp(12px, calc(3.2 * var(--u)), 22px); }

/* ---------------- Tutorial overlay ---------------- */
.tut-card { max-width: 340px; }
.tut-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.tut-counter { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; }
.tut-skip { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 12px; padding: 2px 0; }
.tut-skip:hover { color: var(--ink); }
.tut-title { font-size: 18px; color: var(--accent); margin: 0 0 10px; }
.tut-text { color: #c8b87a; margin: 0 0 16px; line-height: 1.6; font-size: 14px; }
.tut-text strong { color: var(--ink); }
.tut-text em { color: var(--accent-2); font-style: normal; }
.tut-dots { display: flex; gap: 6px; justify-content: center; margin-bottom: 16px; }
.tut-dot { width: 7px; height: 7px; border-radius: 50%; background: #2a3a1a; transition: background 0.2s; }
.tut-dot.active { background: var(--accent-2); }
.tut-actions { display: flex; gap: 8px; justify-content: space-between; align-items: center; }
.tut-prev { background: none; border: 1px solid #314733; border-radius: 8px; color: var(--muted); cursor: pointer; font-size: 13px; padding: 8px 14px; }
.tut-prev:hover { color: var(--ink); border-color: #4a6a3a; }
.tut-next-btn { flex: 1; }

/* ---------------- Contextual tips ---------------- */
.tut-tip {
  position: fixed; left: 50%; transform: translateX(-50%) translateY(8px);
  bottom: calc(env(safe-area-inset-bottom, 0px) + 92px);
  /* 39, one BELOW .modal — a coach mark must never paint over a dialog. At the same
     z-index as .modal it won by being appended to <body> later, and on a phone the
     "Heading out?" tip landed squarely on the offline summary's Continue button: four
     taps in a row did nothing because they were all hitting the tip. tutorial.js also
     holds tips back while any modal is open; this is the backstop for a tip that was
     already on screen when one opened. */
  width: min(92%, 420px); z-index: 39; pointer-events: none;
  opacity: 0; transition: opacity .25s ease, transform .25s ease;
}
.tut-tip.show { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
.tut-tip.hidden { display: none; }
.tut-tip-card {
  background: #16210ff2; border: 1px solid #314733; border-left: 4px solid var(--accent-2);
  border-radius: 12px; padding: 14px 16px; box-shadow: 0 10px 28px rgba(0,0,0,.5);
}
.tut-tip-title { color: var(--accent); font-size: 15px; font-weight: 600; margin-bottom: 6px; }
.tut-tip-text { color: #c8b87a; font-size: 14px; line-height: 1.55; margin-bottom: 12px; }
.tut-tip-text strong { color: var(--ink); }
.tut-tip-text em { color: var(--accent-2); font-style: normal; }
.tut-tip-ok { width: 100%; }

/* ── Item-found discovery popup ("treasure moment") ──────────────────────────────────
   Wrapper covers the screen but is click-through (pointer-events:none) so the popup never
   traps the player; only the card catches taps to dismiss early. Centred a little above the
   middle so it clears the player avatar. Rarity sets --rar / --rarGlow for border + glow. */
.if-wrap {
  position: fixed; inset: 0; z-index: 70;
  display: flex; align-items: center; justify-content: center;
  padding-bottom: 16vh;                 /* nudge the card up, off the avatar/controls */
  pointer-events: none;
}
.if-wrap.hidden { display: none; }

.if-card {
  --rar: #b8c4b0; --rarGlow: rgba(184,196,176,.4); --rarInk: #d8e0d0;
  pointer-events: auto; cursor: pointer;
  position: relative; width: min(90vw, 360px); max-width: 360px;
  padding: 18px 22px 14px; text-align: center;
  background: linear-gradient(180deg, #20271b 0%, #131810 100%);
  border: 2px solid var(--rar);
  border-radius: 16px;
  box-shadow: 0 12px 44px rgba(0,0,0,.6), 0 0 30px var(--rarGlow), inset 0 0 26px rgba(0,0,0,.45);
  overflow: hidden;
  will-change: transform, opacity;
}
.if-card.rar-worn     { --rar:#8b9088; --rarGlow:rgba(139,144,136,.35); --rarInk:#c4c8c0; }
.if-card.rar-standard { --rar:#b8c4b0; --rarGlow:rgba(184,196,176,.4);  --rarInk:#d8e0d0; }
.if-card.rar-refined  { --rar:#7fc9a0; --rarGlow:rgba(127,201,160,.45); --rarInk:#a8e6c4; }
.if-card.rar-rare     { --rar:#5a86c0; --rarGlow:rgba(90,134,192,.55);  --rarInk:#8db4ec; }
.if-card.rar-epic     { --rar:#b46fd0; --rarGlow:rgba(180,111,208,.55); --rarInk:#d29cef; }
.if-card.rar-relic    { --rar:#d8a24a; --rarGlow:rgba(216,162,74,.65);  --rarInk:#f0c878; }

/* entrance: scale + fade in (~0.3s) with a soft overshoot; exit: gentle scale/fade out */
@keyframes if-in  { 0%{opacity:0;transform:scale(.7)} 65%{opacity:1;transform:scale(1.05)} 100%{transform:scale(1)} }
@keyframes if-out { 0%{opacity:1;transform:scale(1)} 100%{opacity:0;transform:scale(.92) translateY(8px)} }
.if-card.if-in  { animation: if-in .32s cubic-bezier(.2,1.2,.4,1) both; }
.if-card.if-out { animation: if-out .4s ease-in both; }

/* faint rotating radiance behind the whole card */
.if-rays {
  position: absolute; left: 50%; top: 38%; width: 460px; height: 460px;
  transform: translate(-50%,-50%); pointer-events: none; opacity: .5;
  background: conic-gradient(from 0deg, transparent 0 14deg, var(--rarGlow) 16deg 18deg, transparent 20deg 44deg);
  -webkit-mask: radial-gradient(circle, #000 0 30%, transparent 62%);
          mask: radial-gradient(circle, #000 0 30%, transparent 62%);
  animation: if-spin 14s linear infinite;
}
@keyframes if-spin { to { transform: translate(-50%,-50%) rotate(360deg); } }

.if-heading {
  position: relative; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  font-size: 13px; color: var(--rarInk); margin-bottom: 8px; text-shadow: 0 1px 3px #000;
}

/* artwork — the visual focus, with a glowing halo + slow magical ring */
.if-art { position: relative; width: 120px; height: 120px; margin: 2px auto 10px; }
.if-glow {
  position: absolute; inset: -8px; border-radius: 50%;
  background: radial-gradient(circle, var(--rarGlow) 0%, transparent 68%);
  animation: if-pulse 2.4s ease-in-out infinite;
}
@keyframes if-pulse { 0%,100%{opacity:.6;transform:scale(.94)} 50%{opacity:1;transform:scale(1.06)} }
.if-ring {
  position: absolute; inset: 4px; border-radius: 50%;
  border: 2px dashed var(--rar); opacity: .55;
  animation: if-spin2 9s linear infinite;
}
@keyframes if-spin2 { to { transform: rotate(360deg); } }
.if-img {
  position: absolute; inset: 0; margin: auto; width: 92px; height: 92px;
  image-rendering: pixelated; object-fit: contain;
  filter: drop-shadow(0 4px 8px rgba(0,0,0,.6)) drop-shadow(0 0 10px var(--rarGlow));
  animation: if-bob 3s ease-in-out infinite;
}
@keyframes if-bob { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-4px)} }

/* one-shot shine sweep across the card on entrance */
.if-shine {
  position: absolute; top: 0; left: -60%; width: 50%; height: 100%; pointer-events: none;
  background: linear-gradient(105deg, transparent 0%, rgba(255,255,255,.18) 50%, transparent 100%);
  transform: skewX(-18deg);
}
.if-card.if-in .if-shine { animation: if-sweep 1s ease-out .15s 1 both; }
@keyframes if-sweep { 0%{left:-60%} 100%{left:130%} }

.if-name {
  position: relative; font-weight: 800; color: var(--ink); line-height: 1.15;
  font-size: clamp(19px, 5.4vw, 24px); text-shadow: 0 1px 4px #000;
  word-break: break-word;
}
.if-rarity {
  position: relative; display: inline-block; margin: 6px 0 2px;
  font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--rarInk); padding: 2px 10px; border: 1px solid var(--rar); border-radius: 999px;
}
.if-desc {
  position: relative; color: #c8c2a8; font-size: 13.5px; line-height: 1.45;
  margin: 8px 4px 10px; font-style: italic;
}
.if-meta {
  position: relative; display: flex; flex-wrap: wrap; gap: 5px 7px; justify-content: center;
  margin-bottom: 8px;
}
.if-meta > span {
  font-size: 12px; font-weight: 600; color: #d8d2bc;
  background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1);
  padding: 2px 8px; border-radius: 6px; display: inline-flex; align-items: center; gap: 3px;
}
.if-meta .ifm-type { color: var(--rarInk); border-color: var(--rar); }
.if-meta .ifm-stat { color: #8fd07f; }
.if-meta .ifm-val { color: var(--accent); }
.if-more { position: relative; font-size: 12px; color: var(--accent-2); font-weight: 700; margin-bottom: 4px; }
.if-more.hidden { display: none; }
.if-hint { position: relative; font-size: 11px; color: #7d8478; letter-spacing: .03em; }

@media (max-width: 480px) {
  .if-wrap { padding-bottom: 18vh; }
  .if-card { width: min(86vw, 330px); padding: 16px 18px 12px; }
  .if-art { width: 104px; height: 104px; }
  .if-img { width: 80px; height: 80px; }
}
@media (prefers-reduced-motion: reduce) {
  .if-rays, .if-ring, .if-img, .if-glow, .if-card.if-in .if-shine { animation: none; }
  .if-card.if-in { animation: if-in .2s ease-out both; }
}

.building-card {
  max-width: 360px;
}
.building-card p {
  margin: 8px 0 14px;
  color: #ded7bd;
  line-height: 1.45;
}
.build-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
}
.build-actions button {
  min-width: 112px;
}
.smith-card { max-width: 620px; }
.smith-lines { color: #ded7bd; line-height: 1.45; margin-bottom: 12px; }
.smith-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.smith-actions button, .smith-recipe button {
  border: 1px solid #6a5734;
  background: #2b2116;
  color: #f2dfad;
  border-radius: 6px;
  padding: 9px 12px;
  font-weight: 800;
}
.smith-recipe {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 10px;
  border-top: 1px solid rgba(242, 223, 173, 0.16);
  padding: 10px 0;
}
.smith-recipe span { color: #bdb394; font-size: 13px; }
.smith-btns { grid-row: 1 / span 3; grid-column: 2; align-self: center; display: flex; flex-direction: column; gap: 6px; }
.smith-btns button { width: 100%; }
.smith-recipe.locked { opacity: 0.55; }
.smith-btns button[disabled] { opacity: 0.5; cursor: not-allowed; }
.smith-card h3 { margin: 14px 0 6px; font-size: 16px; color: #f2dfad; }

/* ---------------- Tempering bench ---------------- */
.temper-card { max-width: 560px; position: relative; } /* keeps .modal-card's internal scroll */
.temper-intro { color: #ded7bd; line-height: 1.45; margin-bottom: 10px; }
.temper-gold {
  display: flex; justify-content: flex-end; align-items: center; gap: 4px;
  color: #bdb394; font-size: 13px; margin: -4px 0 8px;
}
.temper-gold strong { color: #f0c878; font-size: 14px; }
.temper-slots { display: flex; gap: 8px; margin-bottom: 10px; }
.temper-slot {
  flex: 1; min-width: 0; min-height: 86px; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px; padding: 8px 4px; text-align: center;
  border: 1px solid #6a5734; border-left-width: 4px; border-radius: 8px; background: #241c11; cursor: pointer;
}
.temper-slot.empty { border-style: dashed; cursor: default; opacity: 0.7; }
.ts-plus { font-size: 22px; color: #8a7a52; line-height: 1; }
.ts-hint { color: #8a7a52; font-size: 12px; }
.ts-icon { width: 34px; height: 34px; image-rendering: pixelated; }
.ts-name { font-size: 12px; color: #f2dfad; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.ts-rar { font-size: 11px; }
.temper-out { border: 1px solid rgba(242, 223, 173, 0.22); border-radius: 8px; padding: 10px 12px; margin-bottom: 10px; background: #221a10; }
.temper-out.temper-waiting, .temper-out.temper-blocked { color: #bdb394; font-size: 13px; }
.temper-out.temper-blocked { color: #e0a08a; }
.to-row { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; padding: 2px 0; }
.to-row > span { color: #bdb394; font-size: 13px; }
.to-bt { color: #f0c878; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; }
.temper-note { color: #a89c78; font-size: 12px; line-height: 1.4; margin-top: 6px; }
.temper-result { border-radius: 8px; padding: 8px 12px; margin-bottom: 10px; font-size: 13px; line-height: 1.4; }
.temper-result.win { background: rgba(111, 174, 106, 0.16); color: #b5e2ae; border: 1px solid rgba(111, 174, 106, 0.4); }
.temper-result.fail { background: rgba(160, 90, 60, 0.16); color: #e0b49a; border: 1px solid rgba(160, 90, 60, 0.4); }
/* Forge progress while a temper is rolling. The fill's width is set inline from elapsed time
   (see temper-ui.js) — no CSS transition, or the panel's full re-render would restart it. */
.temper-progress { margin-bottom: 10px; }
.tp-label { color: #d8b070; font-size: 12px; letter-spacing: 0.04em; margin-bottom: 5px; }
.tp-track {
  height: 10px; border-radius: 6px; overflow: hidden;
  background: rgba(0, 0, 0, 0.42); border: 1px solid #6a5228;
}
.tp-fill {
  height: 100%; border-radius: 5px 0 0 5px;
  background: linear-gradient(90deg, #8a5a1e, #f0c878 70%, #ffe9b0);
  box-shadow: 0 0 10px rgba(240, 200, 120, 0.55);
}
.temper-go {
  width: 100%; padding: 12px; margin-bottom: 6px; border-radius: 8px; border: 1px solid #8a6a34;
  background: linear-gradient(#4a3a1e, #33260f); color: #ffe9b0; font-size: 17px; font-weight: 800; letter-spacing: 0.02em;
}
.temper-go[disabled] { opacity: 0.5; cursor: not-allowed; }
.temper-controls { display: flex; gap: 8px; margin: 2px 0 8px; }
.tc-field { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--muted); }
.tc-field select {
  width: 100%; padding: 7px 9px; border-radius: 7px; border: 1px solid #6a5734;
  background: #241c11; color: #f2dfad; font-size: 13px; font-weight: 600;
}
.temper-list { display: flex; flex-direction: column; gap: 6px; max-height: 32vh; overflow-y: auto; }
.temper-item {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px; cursor: pointer;
  border: 1px solid rgba(242, 223, 173, 0.14); border-left-width: 4px; border-radius: 8px; background: #241c11;
}
.temper-item button { border: 1px solid #6a5734; background: #2b2116; color: #f2dfad; border-radius: 6px; padding: 7px 12px; font-weight: 800; }
.temper-item button[disabled] { opacity: 0.5; cursor: not-allowed; }
.ti-icon { width: 30px; height: 30px; image-rendering: pixelated; flex: none; }
.ti-body { flex: 1; min-width: 0; }
.ti-name { color: #f2dfad; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ti-sub { font-size: 12px; }
/* rarity name inks inside the temper screen (the shared .rar-* rules only ink .ii-name/.es-item) */
.temper-card .rar-worn { color: #b7c0b0; } .temper-card .rar-standard { color: #e6e6e6; }
.temper-card .rar-refined { color: #93d68c; } .temper-card .rar-rare { color: #8db4ec; }
.temper-card .rar-epic { color: #d29cef; } .temper-card .rar-relic { color: #f0c878; }
/* forge-beat overlay: rising sparks (success) or drifting smoke puffs (failure) */
.temper-fx { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.temper-fx span { position: absolute; bottom: 18%; width: 6px; height: 6px; border-radius: 50%; animation: temper-spark 1.1s ease-out forwards; }
.temper-fx.fx-success span { background: #ffd070; box-shadow: 0 0 8px 2px rgba(255, 180, 60, 0.8); }
.temper-fx.fx-fail span { background: #5a544c; width: 10px; height: 10px; box-shadow: 0 0 10px 4px rgba(70, 66, 60, 0.7); animation-duration: 1.3s; }
@keyframes temper-spark {
  0% { transform: translateY(0) scale(1); opacity: 0; }
  15% { opacity: 1; }
  100% { transform: translateY(-140px) scale(0.4); opacity: 0; }
}

/* ---------------- AFK & Power Saving (js/afk.js) ---------------- */
/* The settings submenu modal: same control language as the ⚙ popover (mp-mute buttons),
   sized for a modal card. Opened by the single "AFK & Power Saving" popover button. */
/* Sound submenu — same shape as the AFK submenu below it. */
.sound-settings { display: flex; flex-direction: column; gap: 8px; text-align: left; }
.sound-settings h2 { margin: 0 0 2px; }
.sound-settings .muted { margin: 0 0 2px; font-size: 12.5px; line-height: 1.35; }
.sound-settings .mp-mute {
  width: 100%; padding: 10px; border-radius: 8px; border: 1px solid #314733;
  background: var(--panel-2); color: var(--ink); cursor: pointer; font-size: 14px; font-weight: 600;
}
.sound-settings .mp-mute.active { border-color: var(--accent-2); color: var(--accent-2); }
.sound-settings .mp-label { color: var(--muted); font-size: 12.5px; margin: 4px 0 -4px; }
.sound-settings .mp-range { width: 100%; accent-color: var(--accent-2); }
.sound-settings .mp-credit { margin-top: 2px; font-size: 10px; color: var(--muted); text-align: center; line-height: 1.3; }

.afk-settings { display: flex; flex-direction: column; gap: 8px; text-align: left; }
.afk-settings h2 { margin: 0 0 2px; }
.afk-settings .mp-mute {
  width: 100%; padding: 10px; border-radius: 8px; border: 1px solid #314733;
  background: var(--panel-2); color: var(--ink); cursor: pointer; font-size: 14px; font-weight: 600;
}
.afk-settings .mp-mute.active { border-color: var(--accent-2); }
.afk-settings .mp-select {
  width: 100%; padding: 9px; border-radius: 8px; border: 1px solid #314733;
  background: var(--panel-2); color: var(--ink); font-size: 14px;
}
.afk-settings .mp-select:disabled, .afk-settings .mp-mute:disabled { opacity: .45; }
.afk-settings .mp-label { color: var(--muted); font-size: 12.5px; margin: 4px 0 -4px; }
.afk-settings .mp-note { margin: -2px 0 0; color: var(--muted); font-size: 11.5px; line-height: 1.35; }
.music-pop .mp-mute.active { border-color: var(--accent-2); }

/* The low-power AFK screen. Pure black base (an OLED power win in itself), no gradients,
   no animation — the only motion is a JS-toggled dot opacity once per slow refresh. */
.afk-screen {
  position: fixed; inset: 0; z-index: 60;
  background: #000;
  display: flex; align-items: center; justify-content: center;
  padding: calc(20px + env(safe-area-inset-top, 0px)) 20px calc(20px + env(safe-area-inset-bottom, 0px));
  cursor: pointer;
}
.afk-inner { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; max-width: 420px; width: 100%; }
.afk-title { color: var(--accent); font-weight: 700; font-size: clamp(20px, calc(5.6 * var(--u)), 34px); letter-spacing: .5px; }
.afk-portrait { width: clamp(72px, calc(20 * var(--u)), 120px); height: clamp(72px, calc(20 * var(--u)), 120px);
  image-rendering: pixelated; border: 1px solid #223528; border-radius: 12px; background: #0a0d08; }
.afk-name { color: var(--ink); font-weight: 700; font-size: clamp(18px, calc(4.8 * var(--u)), 30px); }
.afk-activity { color: var(--muted); font-size: clamp(15px, calc(4 * var(--u)), 24px); }
.afk-conn { display: flex; align-items: center; gap: 8px; font-size: clamp(13px, calc(3.4 * var(--u)), 20px); color: var(--muted); }
.afk-conn-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent-2); opacity: .35; transition: opacity 2.5s linear; }
.afk-conn.tick .afk-conn-dot { opacity: 1; }
.afk-conn.warn .afk-conn-dot { background: #d8b24a; }
.afk-conn.bad .afk-conn-dot { background: var(--danger); }
.afk-conn.bad { color: #d8a08a; }
.afk-duration { color: var(--ink); font-size: clamp(15px, calc(4 * var(--u)), 24px); margin-top: 4px; font-variant-numeric: tabular-nums; }
.afk-hp { color: var(--muted); font-size: clamp(13px, calc(3.4 * var(--u)), 20px); font-variant-numeric: tabular-nums; }
.afk-stats { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; min-height: 1em; }
.afk-stat { color: var(--ink); font-size: clamp(14px, calc(3.8 * var(--u)), 22px); font-variant-numeric: tabular-nums; }
.afk-alert { color: #e8c06a; font-size: clamp(13px, calc(3.6 * var(--u)), 21px); border: 1px solid #4a3f1e;
  border-radius: 10px; padding: 8px 12px; background: #14100a; margin-top: 4px; }
.afk-return {
  margin-top: 14px; width: 100%; max-width: 320px;
  padding: clamp(14px, calc(3.6 * var(--u)), 22px);
  border-radius: 12px; border: 1px solid #4a5a3a; background: #22301c; color: #e8d9a0;
  font-size: clamp(17px, calc(4.6 * var(--u)), 28px); font-weight: 700; cursor: pointer;
}
.afk-hint { color: #4a5647; font-size: clamp(11px, calc(3 * var(--u)), 17px); }
/* Dim: an extra-dark presentation for phones left face-up. Static opacity only. */
.afk-screen.dim .afk-inner { opacity: .5; }
/* Waking hold: exit was tapped; keep covering the world until fresh data has landed. */
.afk-screen.waking .afk-inner { opacity: .35; }

/* ── Graphics settings screen (js/graphics-ui.js) ─────────────────────────────────────
   Phone-portrait first: one column of compact rows, collapsible categories, segmented
   selectors that wrap rather than shrinking below a comfortable touch target, and a
   sticky action bar so Apply/Cancel stay reachable however long the list gets. */
.gfx-card { max-width: 420px; }
.gfx-head { display: flex; align-items: center; gap: 6px; }
.gfx-head h2 { margin: 0 0 10px; }
.gfx-backbtn {
  background: none; border: none; color: var(--accent); cursor: pointer;
  font-size: 30px; line-height: 1; padding: 0 8px 10px 0; min-width: 34px;
}
.gfx-intro { margin: 0 0 12px; font-size: 12px; line-height: 1.4; }

/* Collapsible category. The body is display:none when closed rather than max-height
   animated — with UI animation off there'd be nothing to animate anyway, and a hard
   toggle can't leave a half-open section behind. */
.gfx-sec { border: 1px solid #314733; border-radius: 10px; background: #0e130d; margin: 0 0 8px; }
.gfx-sechead {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  padding: 11px 12px; background: none; border: none; cursor: pointer; color: var(--ink);
  font-size: 14px; font-weight: 700; text-align: left;
}
.gfx-sectitle { color: var(--accent); }
.gfx-secarrow { color: var(--muted); transition: transform .18s ease; }
.gfx-sec.open .gfx-secarrow { transform: rotate(180deg); }
.gfx-secbody { display: none; padding: 0 12px 12px; }
.gfx-sec.open .gfx-secbody { display: block; }

.gfx-row { padding: 8px 0; border-top: 1px solid #1e2a1c; }
.gfx-row:first-child { border-top: none; }
.gfx-rowlabel { font-size: 13px; font-weight: 600; color: var(--ink); margin: 0 0 6px; }
.gfx-note { margin: 6px 0 0; font-size: 11px; line-height: 1.35; color: var(--muted); }

/* Segmented selector. Equal-width columns on one row: five options across a 412px phone
   is ~64px each, comfortably above the ~44px touch target, and a single row reads as one
   scale (which is what these settings are) instead of a 4+1 block. Very narrow screens
   fall back to wrapping so nothing is ever clipped. */
.gfx-seg { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 4px; }
.gfx-segbtn {
  /* Two-word labels ("Very Low", "Ultra Low") wrap onto a second line rather than being
     truncated — grid rows size to the tallest button, so the strip stays even. */
  min-height: 38px; padding: 5px 3px; line-height: 1.15;
  border: 1px solid #314733; border-radius: 8px; background: var(--panel-2);
  color: var(--ink); font-size: 11px; font-weight: 600; cursor: pointer;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
@media (max-width: 359px) {
  .gfx-seg { display: flex; flex-wrap: wrap; }
  .gfx-segbtn { flex: 1 1 30%; }
}
.gfx-segbtn:hover { border-color: var(--accent); }
.gfx-segbtn.active { border-color: var(--accent); background: #2c3f2a; color: var(--accent); }

/* Switch row: full-width hit target with the label on the left and the track on the right. */
.gfx-switch {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; min-height: 40px; padding: 6px 0;
  background: none; border: none; cursor: pointer; color: var(--ink);
  font-size: 13px; font-weight: 600; text-align: left;
}
.gfx-switch-label { flex: 1; min-width: 0; }
.gfx-switch-track {
  flex: 0 0 auto; position: relative; width: 42px; height: 24px; border-radius: 999px;
  border: 1px solid #314733; background: #11160f; transition: background .15s ease, border-color .15s ease;
}
.gfx-switch-knob {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: #6b7a66; transition: transform .15s ease, background .15s ease;
}
.gfx-switch.on .gfx-switch-track { background: #2c3f2a; border-color: var(--accent); }
.gfx-switch.on .gfx-switch-knob { transform: translateX(18px); background: var(--accent); }

/* Low-Power Mode: the one control on this screen that gets visual weight of its own. */
.gfx-lowpower {
  margin: 10px 0 0; padding: 8px 12px; border: 1px solid #4a5c3a; border-radius: 10px;
  background: #12180f;
}
.gfx-lowpower.on { border-color: var(--accent); background: #1a2113; }

.gfx-actions { display: flex; gap: 8px; margin-top: 14px; }
.gfx-actions button { flex: 1; min-height: 42px; }
.gfx-actions-reset { margin-top: 8px; }
.gfx-resetbtn {
  border: 1px solid #314733; border-radius: 10px; background: none;
  color: var(--muted); font-size: 12px; font-weight: 600; padding: 9px 6px; cursor: pointer;
}
.gfx-resetbtn:hover { border-color: var(--accent); color: var(--ink); }

/* ── UI Animation setting (body classes, set by js/graphics.js) ───────────────────────
   Only durations are neutralised — never `display`, `visibility` or `opacity` end states —
   so every menu, popup and toast still appears, still closes, and still responds. An
   element that relies on a transition to become visible ends up visible instantly rather
   than never. `gfx-no-motion` covers the "Motion effects" toggle: the ambient drifting /
   breathing overlays (dread vignette, announcement pulses) hold still instead of moving,
   but keep their intensity, because those are danger cues rather than decoration. */
body.gfx-ui-off *, body.gfx-ui-off *::before, body.gfx-ui-off *::after {
  transition-duration: 0s !important;
  animation-duration: 0s !important;
  animation-iteration-count: 1 !important;
}
body.gfx-ui-reduced *, body.gfx-ui-reduced *::before, body.gfx-ui-reduced *::after {
  transition-duration: .06s !important;
  animation-duration: .18s !important;
}
/* …with one exception, because that blanket duration is not as harmless as it looks:
   `transition-property` defaults to `all`, so handing a duration to every element makes
   properties that were never meant to animate start interpolating. On the minimap that was
   a real bug — minimap.js sizes #minimap-body from JS and welds a Phaser camera to its rect,
   so an interpolated height meant the camera measured a box on its way from 72vh down to the
   dial, and the map rendered from the dial all the way down the screen. Nothing here should
   ever tween; the frame is a live camera surface, not decoration. Id specificity beats the
   `body.gfx-ui-* *` rules above, so this holds at every UI-animation level. */
#minimap, #minimap * { transition-property: none !important; }
/* The always-on HUD warnings get their opacity FROM their keyframes, so simply zeroing the
   duration above would leave them at full strength rather than their intended resting level.
   These are the same calm static states the prefers-reduced-motion block already defines —
   the danger cue stays fully visible, it just stops throbbing. */
body.gfx-ui-off .lowhp-vignette, body.gfx-no-motion .lowhp-vignette {
  animation: none !important; opacity: .72;
}
body.gfx-ui-off #hp-bar.low, body.gfx-no-motion #hp-bar.low {
  animation: none !important; box-shadow: 0 0 8px 1px rgba(210,74,74,.5);
}
body.gfx-ui-off #hp-bar.healing, body.gfx-no-motion #hp-bar.healing {
  animation: none !important; box-shadow: 0 0 8px 1px rgba(90,200,90,.45);
}
/* Dread stays fully visible — the darkness is a danger cue, the movement is the garnish. */
body.gfx-no-motion .dread-vignette { animation: none !important; }
body.gfx-no-motion .dread-vignette::after { animation: none !important; opacity: .5; }

/* ---------------- Onboarding coaching (js/onboarding.js) ----------------
   A ring around the control the beginner quest is asking for, plus one short line beside
   it. Deliberately small: no scrim, no blocking overlay, nothing that has to be dismissed —
   a new player can ignore it and keep playing, which is the whole point of an idle game.
   The ring is drawn OUTSIDE the control's box (inset ring + offset shadow, never a fill) so
   it can't swallow the tap it is asking for; pointer-events stay off for the same reason. */
.coach-ring {
  position: fixed; z-index: 60; pointer-events: none;
  border: 2px solid var(--accent); border-radius: 14px;
  box-shadow: 0 0 0 2px #0008, 0 0 14px 2px rgba(201,160,60,.45);
  animation: coach-pulse 1.9s ease-in-out infinite;
}
.coach-ring.hidden, .coach-chip.hidden { display: none; }
@keyframes coach-pulse {
  0%, 100% { opacity: .95; transform: scale(1); }
  50%      { opacity: .55; transform: scale(1.035); }
}
/* The line itself: the same dark parchment chip the rest of the HUD uses, capped narrow so
   it stays one or two lines on a 320px screen. */
.coach-chip {
  /* Transparent to taps. The chip floats over the world, and on a desktop viewport it landed
     squarely on top of Mira and ate the tap the tutorial was asking for — caught by the
     end-to-end test, which is exactly the class of thing a player would have silently given
     up over. Only the button inside it is clickable. */
  pointer-events: none;
  position: fixed; z-index: 61; max-width: min(78vw, 340px);
  display: flex; align-items: center; gap: 10px;
  padding: clamp(7px, calc(1.7 * var(--u)), 13px) clamp(10px, calc(2.4 * var(--u)), 16px);
  background: linear-gradient(#1b2216f2, #141a10ee);
  border: 1px solid #4b5c39; border-left: 3px solid var(--accent);
  border-radius: 11px; box-shadow: 0 4px 18px #0009;
  color: var(--ink); font-size: clamp(13px, calc(3.2 * var(--u)), 19px); line-height: 1.35;
}
.coach-chip .coach-text { flex: 1 1 auto; }
.coach-chip .coach-show {
  pointer-events: auto;   /* the one part of the chip that IS a control */
  flex: 0 0 auto; padding: 6px 11px; min-height: 34px;   /* thumb-sized on a phone */
  border: 1px solid var(--accent); border-radius: 8px; background: #2a3320;
  color: var(--accent-2); font-weight: 700; font-size: .92em; cursor: pointer;
}
.coach-chip .coach-show:active { background: #38431f; }
.coach-chip .coach-show.hidden { display: none; }

