/* Play, self-hosted (SIL OFL; woff2 subsets from Google Fonts v21). Local files
   because Poki BLOCKS all external requests including Google Fonts - and every other
   platform simply loads faster without the googleapis round-trips. Latin covers en,
   cyrillic covers ru/uk; the other i18n scripts fall back to system fonts as before. */
@font-face {
  font-family: "Play"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(assets/fonts/play-400-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122;
}
@font-face {
  font-family: "Play"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(assets/fonts/play-400-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Play"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(assets/fonts/play-700-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122;
}
@font-face {
  font-family: "Play"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(assets/fonts/play-700-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* ===========================================================================
   BlockCraft UI — Minecraft GUI style.
   Look is built from sharp pixel bevels (0-blur inset box-shadows), the vanilla
   gray panel/slot palette, stone buttons with a black outline, the dark hotbar
   with a white selection frame, and MC purple-bordered tooltips. No rounded
   corners anywhere; everything renders crisp + pixelated.
   ========================================================================= */

:root {
  /* slot sizing: EVERY inventory/furnace/chest slot derives from these (the
     compact media block below shrinks them for phones - both narrow portrait
     AND low landscape, where width alone never triggered the old query) */
  --slot: 44px;               /* inventory slot square */
  --hb-slot: 42px;            /* hotbar slot square; #survHud spans 9 of these */
  --icon: 34px;               /* item icon inside a slot */
  --pad: 5px;                 /* icon inset = (slot - icon) / 2 */
  --mc-panel: #c6c6c6;        /* light inventory panel */
  --mc-panel-dark: #313233;   /* pause / settings / tnt panels */
  --mc-slot: #8b8b8b;         /* recessed inventory slot */
  --mc-shadow: #373737;       /* bevel shadow (top-left of recessed) */
  --mc-hi: #ffffff;           /* bevel highlight */
  --mc-lo: #565656;           /* bevel low */
  --mc-btn: #6c6c6c;          /* stone button */
  --mc-btn-hi: #7f8a7f;       /* stone button hover */
  --mc-green: #5b9c34;        /* MC grass green (confirm / on) */
  --mc-green-d: #4a7f29;
  --mc-tip-bg: rgba(18,0,24,0.92);
  --mc-tip-bd: #3b0a78;
  --mc-text: #ffffff;
  --mc-text-dark: #3a3a3a;
}

html, body {
  margin: 0; padding: 0;
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  overscroll-behavior-y: none;
  background: #0b0e14;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
  font-family: "Play", "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
  color: #fff;
}

/* kill the iOS long-press magnifier / selection loupe everywhere (it appears on
   touch-hold even with body-level rules unless every element opts out) */
* {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

#gl {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  touch-action: none;
}
/* focusable (tabindex=-1) so we can hand it keyboard focus when the pointer
   locks - a locked pointer feeds the mouse but NOT the keyboard; no focus ring */
#gl:focus, #gl:focus-visible { outline: none; }

/* MC drop-shadow on text by default; panels with dark backgrounds rely on it */
.modalTitle, .setLabel, .primaryBtn, .secondaryBtn, .distChip, .togBtn,
#sensVal, #invHint, #tntDesc, #loadTitle, #loadLabel, #hintTop, #toast,
.hbSlot .num, .countPill, .modeLabel, #playLabel {
  text-shadow: 2px 2px 0 rgba(0,0,0,0.55);
}

/* ---------- HUD ---------- */

#hud { position: absolute; inset: 0; pointer-events: none; }

#crosshair {
  position: absolute; left: 50%; top: 50%;
  width: 18px; height: 18px;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
#crosshair::before, #crosshair::after {
  content: ""; position: absolute; background: #fff;
  mix-blend-mode: difference;
  image-rendering: pixelated;
}
#crosshair::before { left: 8px; top: 0; width: 2px; height: 18px; }
#crosshair::after { left: 0; top: 8px; width: 18px; height: 2px; }

/* attack-cooldown charge meter (MC 1.9 combat): a small bar under the
   crosshair, visible only while the weapon is recharging (survival) */
#atkMeter {
  position: absolute; left: 50%; top: calc(50% + 20px);
  transform: translateX(-50%);
  width: 30px; height: 4px;
  background: rgba(0, 0, 0, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.55);
  pointer-events: none;
}
#atkMeterFill { height: 100%; width: 0; background: #e8e8e8; }
/* .hidden is styled PER ELEMENT in this file (no global rule) - without this
   line the meter never actually disappeared */
#atkMeter.hidden { display: none; }

/* hint + toast use the vanilla item-tooltip look */
#hintTop {
  position: absolute; left: 50%; top: 12px;
  transform: translateX(-50%);
  background: var(--mc-tip-bg);
  border: 2px solid var(--mc-tip-bd);
  border-radius: 0;
  padding: 6px 14px;
  font-size: 14px;
  white-space: nowrap;
  opacity: 1;
  transition: opacity 1s;
  pointer-events: none;
  max-width: 92vw;
  overflow: hidden;
  text-overflow: ellipsis;
}
#hintTop.faded { opacity: 0; }

/* the get-off hint, shown for as long as you are at a helm or in a seat. It never
   fades: the whole point is that a player who forgot the key can always find it.
   Sits above the health/hunger rows so it never covers them. */
#exitHint {
  position: absolute; left: 50%; bottom: 108px;
  transform: translateX(-50%);
  /* bare text, no plate (owner). It sits over the world instead of over a panel, so
     it needs its own contrast: a hard shadow reads on both a bright sky and a night
     ocean, which a semi-transparent box would not do any better. */
  font-size: 13px;
  color: #e8e8e8;
  text-shadow: 1px 1px 0 #000, -1px 1px 0 #000, 1px -1px 0 #000, -1px -1px 0 #000;
  white-space: nowrap;
  pointer-events: none;
  display: none;
}
body.sailing #exitHint, body.riding #exitHint { display: block; }
body.mobile #exitHint { display: none; }   /* touch exits with the on-screen button */

/* ---- contextual mod promo (promo.js) ----
   Bottom-LEFT, clear of the crosshair and of the hotbar: this interrupts play, so it
   must never sit where the player is aiming. pointer-events are on (unlike the hints) -
   the button is the entire point of the card. */
#promoCard {
  /* TOP-left, under the settings button - NOT bottom-left, which is where #joyZone
     lives on touch (left 30% x bottom 55%, pointer-events auto). A card there sits on
     the movement stick and on the half of the screen the player aims with, so touches
     meant for the world land on the card instead: "can't break blocks down here",
     because the only place the card appears is deep underground. */
  position: absolute; left: 12px; top: 64px;
  display: flex; align-items: center; gap: 10px;
  max-width: 320px;
  padding: 8px 10px;
  z-index: 11;   /* above #joyZone, which now covers this corner on touch */
  background: var(--mc-tip-bg);
  border: 2px solid var(--mc-tip-bd);
  pointer-events: auto;
  animation: promoIn 260ms ease-out;
}
#promoCard.hidden { display: none; }
@keyframes promoIn { from { opacity: 0; transform: translateY(-14px); } to { opacity: 1; transform: none; } }
#promoIcon {
  width: 48px; height: 48px; flex: 0 0 48px;
  image-rendering: pixelated;
}
#promoBody { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
#promoText { font-size: 12px; line-height: 1.35; color: var(--mc-text); }
#promoBtn {
  align-self: flex-start;
  font: inherit; font-size: 12px;
  padding: 4px 12px;
  color: #fff; background: #6d28d9; border: 2px solid #4c1d95;
  cursor: pointer;
}
#promoBtn:hover { background: #7c3aed; }
/* The dismiss control doubles as the countdown: a ring that fills while the card is up,
   so the card visibly has an end instead of just vanishing. Clicking it still closes.
   promo.js drives the duration from SHOW_SEC, so the two can never drift apart. */
#promoClose {
  align-self: flex-start;
  padding: 0; margin-left: 2px;
  background: none; border: 0; cursor: pointer; line-height: 0;
}
#promoClose svg { width: 20px; height: 20px; transform: rotate(-90deg); }
/* phone: the same card ~20% down (owner tried both sizes: the compact one is right on a
   phone and too small on a desktop monitor, so the split lives here, not in the base) */
body.mobile #promoCard { max-width: 256px; gap: 8px; padding: 6px 8px; }
body.mobile #promoIcon { width: 38px; height: 38px; flex: 0 0 38px; }
body.mobile #promoBody { gap: 5px; }
body.mobile #promoText { font-size: 10px; }
body.mobile #promoBtn { font-size: 10px; padding: 3px 10px; }
body.mobile #promoClose svg { width: 16px; height: 16px; }
.promoRingBg, #promoRing { fill: none; stroke-width: 3; }
.promoRingBg { stroke: rgba(255,255,255,0.16); }
#promoRing {
  stroke: #a78bfa; stroke-linecap: round;
  stroke-dasharray: 62.83;      /* 2*pi*r, r=10 */
  stroke-dashoffset: 62.83;
}
#promoClose:hover #promoRing { stroke: #fff; }
@keyframes promoRing { to { stroke-dashoffset: 0; } }

/* The icon has to MOVE. A still 48px sprite next to a price reads as a banner ad; the
   same sprite breathing reads as a piece of the game asking a question. One idle loop
   per mod, chosen in PROMOS[].anim - cheap CSS, no extra assets, no second GL context.
   (A properly animated shark means either a mini-renderer in the card or a baked sprite
   strip; worth building only once these are known to convert.) */
@keyframes promoSwim { 0%,100% { transform: translateX(-3px) rotate(-5deg); } 50% { transform: translateX(3px) rotate(5deg); } }
@keyframes promoRock { 0%,100% { transform: rotate(-7deg); } 50% { transform: rotate(7deg); } }
@keyframes promoFlicker { 0%,100% { opacity: 1; filter: brightness(1.15); } 45% { opacity: .82; filter: brightness(.9); } 70% { opacity: 1; filter: brightness(1.3); } }
@keyframes promoFall { 0% { transform: translateY(-4px) rotate(-6deg); } 100% { transform: translateY(4px) rotate(6deg); } }
@keyframes promoSpin { 0%,100% { transform: rotate(-16deg); } 50% { transform: rotate(16deg); } }
@keyframes promoPulse { 0%,100% { transform: scale(1); filter: brightness(1); } 50% { transform: scale(1.12); filter: brightness(1.35); } }
.promoAnim-swim    { animation: promoSwim    1.6s ease-in-out infinite; }
.promoAnim-rock    { animation: promoRock    2.4s ease-in-out infinite; }
.promoAnim-flicker { animation: promoFlicker 1.1s ease-in-out infinite; }
.promoAnim-fall    { animation: promoFall    1.3s ease-in-out infinite alternate; }
.promoAnim-spin    { animation: promoSpin    2.8s ease-in-out infinite; }
.promoAnim-pulse   { animation: promoPulse   1.5s ease-in-out infinite; }

#hotbar {
  position: absolute; left: 50%; bottom: 10px;
  transform: translateX(-50%);
  display: flex; gap: 0;
  padding: 3px;
  background: rgba(0,0,0,0.55);
  border: 2px solid #1d1d1d;
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.12);
  pointer-events: auto;
}
.hbSlot {
  position: relative;
  width: var(--hb-slot); height: var(--hb-slot);
  background: rgba(28,30,36,0.55);
  border: 2px solid #3a3a3a;
  border-radius: 0;
  box-sizing: border-box;
  cursor: pointer;
}
/* X-ray toggle cell — a standalone hotbar-slot lookalike sitting to the right of
   the hotbar (half the bar = 9*slot/2 + frame; +14px gap). Dim icon = view off,
   bright = on. Hidden via .hidden when the mod is locked. */
#xrayCell {
  position: absolute; bottom: 13px;   /* align with the hotbar SLOTS (bar has 3px padding) */
  left: calc(50% + (9 * var(--hb-slot) + 10px) / 2 + 14px);
  width: var(--hb-slot); height: var(--hb-slot);
  background: rgba(0,0,0,0.55);
  border: 2px solid #1d1d1d;
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.12);
  box-sizing: border-box; z-index: 10; pointer-events: auto; cursor: pointer;
}
#xrayCell.hidden { display: none; }
#xrayCell img {
  position: absolute; inset: 6px;
  width: calc(100% - 12px); height: calc(100% - 12px);
  image-rendering: pixelated; pointer-events: none;
  opacity: 0.32; filter: grayscale(0.5);
}
#xrayCell.active img { opacity: 1; filter: none; }
#xrayCell .num {
  position: absolute; left: 3px; top: 0;
  font-size: 11px; color: rgba(255,255,255,0.75);
  pointer-events: none; z-index: 1;
}
#xrayLabel {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(100% + 2px); font-size: 10px; letter-spacing: 0.5px;   /* ABOVE the cell (stays on-screen) */
  color: rgba(255,255,255,0.55); pointer-events: none; white-space: nowrap;
}
#xrayCell.active #xrayLabel { color: rgba(120,220,150,0.95); }
.hbSlot.sel {
  border-color: #f4f4f4;
  box-shadow: inset 0 0 0 1px #f4f4f4, 0 0 0 1px #f4f4f4;
  z-index: 2;
}
.hbSlot img {
  position: absolute; inset: 6px;
  width: calc(100% - 12px); height: calc(100% - 12px);
  image-rendering: pixelated;
  pointer-events: none;
}
.hbSlot .num {
  position: absolute; left: 3px; top: 0;
  font-size: 11px; color: rgba(255,255,255,0.75);
  pointer-events: none;
}
/* Item cooldown sweep (ender pearl): vanilla covers the slot with a pale wash
   that drains downward as the cooldown runs out. Painted per frame by
   renderPearlCooldown (hud.js) - the hotbar itself is only rebuilt on change. */
.cdSweep {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: rgba(255,255,255,0.35);
  pointer-events: none; z-index: 3;
}
.countPill {
  position: absolute; right: -3px; bottom: -2px;
  background: none; color: #fff;
  border-radius: 0;
  font-size: 13px; font-weight: bold;
  padding: 0;
  pointer-events: none;
}
.adBadge {
  display: inline-block;
  background: #d8741a; color: #fff;
  border: 1px solid #000;
  border-radius: 0;
  font-size: 10px; font-weight: bold;
  letter-spacing: 1px;
  padding: 0 3px;
  font-family: "Play", Arial, sans-serif;
  vertical-align: middle;
}
.hbSlot .adBadge, .invCell .adBadge {
  position: absolute; right: -3px; top: -5px;
}

/* top-corner buttons: square MC stone buttons */
#btnInv {
  position: absolute; right: 12px; top: 12px;
  width: 44px; height: 44px;
  pointer-events: auto;
}
#btnSettings {
  position: absolute; left: 12px; top: 12px;
  width: 44px; height: 44px;
  pointer-events: auto;
}
/* "Remove ads" purchase: same 44x44 box, left of the inventory button (12 + 44
   + 8), so it sits above the minimap row and never overlaps it (owner). */
#btnNoAds {
  position: absolute; right: 64px; top: 12px;
  width: 44px; height: 44px;
  pointer-events: auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 0; overflow: hidden;
}
#btnNoAds.hidden { display: none; }
/* camera view: same 44x44 box, one slot further left again (12 + 2*(44+8)) */
#btnCam {
  position: absolute; right: 116px; top: 12px;
  width: 44px; height: 44px;
  pointer-events: auto;
}
#btnNoAdsMenu.hidden { display: none; }
#btnNoAds .noAdsIcon {   /* owner's 16x16 pixel "no ads" icon, scaled up crisp */
  width: 26px; height: 26px; image-rendering: pixelated; pointer-events: none;
}
#btnNoAds .noAdsPrice {   /* localized price under the icon */
  font-size: 8px; line-height: 1; font-weight: bold; margin-top: 1px;
  color: #ffe08a; text-shadow: 1px 1px 0 rgba(0,0,0,0.7);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  pointer-events: none;
}
/* speech-bubble promo next to the button, shown briefly after each interstitial */
#noAdsPromo {
  position: absolute; right: 12px; top: 62px;
  max-width: 168px; padding: 7px 10px;
  background: var(--mc-btn); border: 2px solid #000;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.4), inset -2px -2px 0 rgba(0,0,0,0.5), 0 2px 6px rgba(0,0,0,0.45);
  color: #fff; font-size: 11px; line-height: 1.25; text-align: center;
  pointer-events: auto; cursor: pointer; z-index: 11;
}
/* upward pointer that ties the bubble to the remove-ads button above it. The button
   sits at right:64..108 (centre right:86); the bubble's right edge is at right:12, so
   the tip is ~74px in from the bubble's right edge = under the button. Two stacked
   triangles = black outline + mc-btn fill, matching the pixel-art button border. */
#noAdsPromo::before {
  content: ""; position: absolute; top: -9px; right: 66px;
  border-left: 8px solid transparent; border-right: 8px solid transparent;
  border-bottom: 9px solid #000;
}
#noAdsPromo::after {
  content: ""; position: absolute; top: -6px; right: 68px;
  border-left: 6px solid transparent; border-right: 6px solid transparent;
  border-bottom: 7px solid var(--mc-btn);
}
#noAdsPromo.hidden { display: none; }
/* desktop shows the menu button too now, for the ESC key hint (owner) */

/* ---------- Settings rows ---------- */

.setRow {
  display: flex; align-items: center; gap: 10px;
  margin: 9px 0;
  text-align: left;
}
.setLabel { flex: 1; font-size: 13px; color: #e9e9e9; }
#distChips { display: flex; gap: 5px; flex-wrap: wrap; justify-content: flex-end; }
.distChip {
  min-width: 30px; padding: 6px 0;
  font-size: 12px;
}
/* distances above the device memory ceiling (settings.js maxRenderDist) */
.distChip:disabled { opacity: 0.35; cursor: default; pointer-events: none; }
#sensRange {
  -webkit-appearance: none; appearance: none;
  width: 140px; height: 14px;
  background: var(--mc-slot);
  border: 2px solid #000; border-radius: 0;
  box-shadow: inset 2px 2px 0 var(--mc-shadow), inset -2px -2px 0 var(--mc-hi);
  cursor: pointer;
}
#sensRange::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px; height: 24px;
  background: var(--mc-btn);
  border: 2px solid #000;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.45), inset -2px -2px 0 rgba(0,0,0,0.5);
  cursor: pointer;
}
#sensRange::-moz-range-thumb {
  width: 12px; height: 24px;
  background: var(--mc-btn);
  border: 2px solid #000; border-radius: 0;
}
#sensVal { min-width: 38px; font-size: 12px; color: #cfcfcf; }
.togBtn {
  min-width: 54px; padding: 6px 10px;
  font-size: 12px;
}

/* ---------- Hand ---------- */

#handBlock {
  position: absolute; right: -34px; bottom: -30px;
  width: 230px; height: 230px;
  pointer-events: none;
  image-rendering: pixelated;
  filter: drop-shadow(0 4px 6px rgba(0,0,0,0.45));
  z-index: 5;
}
body.mobile #handBlock { width: 150px; height: 150px; right: -22px; bottom: -18px; }
#btnJump, #btnDown, #btnMode, #hotbar { z-index: 10; position: absolute; }

/* ---------- Mobile controls ---------- */

#joyZone {
  position: absolute; left: 0; bottom: 0;
  /* narrowed 45% -> 35% -> 30% so the right 70% of the screen is free for block-place
     taps (owner: the joystick zone was still eating placement taps on the left) */
  /* FULL left column since the stick floats (owner: presses above the old 55% band
     span the camera instead of moving). top 64 keeps the settings button's corner
     tappable - the zone would otherwise sit over it in DOM order. The promo card also
     lives inside this rectangle now; it wins by z-index below. */
  width: 30%; top: 64px;
  pointer-events: auto;
  display: none;
  touch-action: none;
}
#joyBase {
  position: absolute; left: 28px; bottom: 84px;
  width: 116px; height: 116px;
  border-radius: 50%;
  background: rgba(255,255,255,0.10);
  border: 2px solid rgba(255,255,255,0.25);
  box-sizing: border-box;
}
#joyKnob {
  position: absolute; left: 50%; top: 50%;
  width: 52px; height: 52px;
  border-radius: 50%;
  background: rgba(255,255,255,0.35);
  transform: translate(-50%, -50%);
}

#btnJump, #btnDown, #btnMode {
  position: absolute;
  pointer-events: auto;
  display: none;
  touch-action: none;
}
#btnJump { right: 22px; bottom: 96px; width: 72px; height: 72px; border-radius: 50%; }
#btnDown { right: 108px; bottom: 84px; width: 56px; height: 56px; border-radius: 50%; }
#btnMode { right: 30px; bottom: 192px; width: 58px; height: 58px; font-size: 10px; }

/* round translucent touch buttons keep their shape; square MC look only for
   the top inv/settings buttons + the mode button */
#btnJump, #btnDown {
  background: rgba(20,22,30,0.55);
  border: 2px solid rgba(255,255,255,0.3);
  color: #fff;
  font-family: inherit;
  cursor: pointer;
}
.keyHint {
  position: absolute; right: 1px; bottom: 0;
  font-size: 9px; line-height: 1; font-weight: bold;
  color: rgba(255,255,255,0.85); text-shadow: 1px 1px 0 rgba(0,0,0,0.7);
  pointer-events: none;
}
body.mobile .keyHint { display: none; }   /* no keyboard on touch */
#btnInv, #btnSettings, #btnMode, #btnNoAds, #btnCam {
  background: var(--mc-btn);
  border: 2px solid #000;
  border-radius: 0;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.4), inset -2px -2px 0 rgba(0,0,0,0.5);
  color: #fff;
  font-family: inherit;
  cursor: pointer;
}
#btnInv:active, #btnSettings:active, #btnMode:active, #btnNoAds:active, #btnCam:active {
  box-shadow: inset -2px -2px 0 rgba(255,255,255,0.35), inset 2px 2px 0 rgba(0,0,0,0.5);
}
#btnJump:active, #btnDown:active { background: rgba(90,96,120,0.7); }
/* riding a vehicle: the jump button turns into "get out" (exit-door glyph);
   body.riding is toggled by vehicles.js, same pattern as body.flying */
#btnJump .icoExit { display: none; }
body.riding #btnJump .icoJump { display: none; }
body.riding #btnJump .icoExit { display: block; }
#btnMode.placeMode { border-color: #000; box-shadow: inset 0 0 0 2px #6fb1ff, inset 2px 2px 0 rgba(255,255,255,0.35); }
#btnMode.breakMode { border-color: #000; box-shadow: inset 0 0 0 2px #ff9a6f, inset 2px 2px 0 rgba(255,255,255,0.35); }
#btnJump svg, #btnDown svg, #btnInv svg, #btnSettings svg, #btnMode svg, #btnCam svg { width: 60%; height: 60%; pointer-events: none; }
#btnMode .modeLabel { display: block; font-size: 9px; margin-top: -4px; pointer-events: none; }

body.mobile #joyZone, body.mobile #btnJump { display: block; }
/* Variant A aims by finger, not a centre crosshair -> hide it on touch */
body.mobile #crosshair { display: none; }
/* the down button is dual-purpose on touch: DESCEND while flying, SNEAK on foot
   (same "go down" glyph). Shown on mobile whenever there is ground movement to
   sneak on — i.e. always except when seated in a vehicle. */
body.mobile #btnDown { display: block; }
/* seated in a boat/minecart the down button is meaningless — hide it even if a
   stale flying state would show it (belt for the flying=false-on-mount fix) */
body.mobile.riding #btnDown { display: none; }
/* engaged-sneak feedback: same pressed look the buttons use */
body.sneaking #btnDown { background: rgba(90,96,120,0.7); border-color: #6fb1ff; }
body.mobile { --hb-slot: 40px; }
/* no keyboard on touch -> the 1-9 hotbar labels are useless (owner) */
body.mobile .hbSlot .num { display: none; }
body.mobile #hotbar { bottom: 6px; }

/* Inventory uses ONE fixed-proportion layout on every device (see .invBox /
   #invGrid below) so touch and desktop look identical. No mobile overrides. */

@media (max-width: 480px) {
  :root, body.mobile { --hb-slot: 34px; }
  .hbSlot .num { font-size: 9px; }
}

/* ---------- Modals (cover full iframe) ---------- */

.modal {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.66);
  display: flex; align-items: center; justify-content: center;
  z-index: 50;
}
.modal.hidden { display: none; }

/* dark stone panel for pause / settings / tnt */
.modalBox {
  background: var(--mc-panel-dark);
  border: 2px solid #000;
  border-radius: 0;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.22), inset -2px -2px 0 rgba(0,0,0,0.5);
  padding: 14px 16px;
  max-width: min(92vw, 520px);
  max-height: 88vh;
  overflow: auto;
  box-sizing: border-box;
  text-align: center;
  image-rendering: pixelated;
}
.modalTitle { font-size: 17px; font-weight: bold; margin-bottom: 10px; letter-spacing: 1px; }
#tntDesc { font-size: 13px; color: #e0e0e0; margin-bottom: 14px; }

.invButtons { display: flex; gap: 8px; justify-content: center; margin-top: 12px; flex-wrap: wrap; }

/* shared MC stone-button look: black outline, two-tone gray (lighter top half),
   crisp 1px bevel, white text with drop-shadow */
.primaryBtn, .secondaryBtn, .distChip, .togBtn, .menuBtn {
  border: 2px solid #000;
  border-radius: 0;
  padding: 7px 14px;
  font-size: 13px; font-weight: bold;
  font-family: inherit;
  color: #fff;
  cursor: pointer;
  background: linear-gradient(180deg, #8c8c8c 0 50%, #6f6f6f 50% 100%);
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.5), inset -1px -1px 0 rgba(0,0,0,0.5);
  text-shadow: 1px 1px 0 rgba(0,0,0,0.6);
  image-rendering: pixelated;
}
.secondaryBtn:hover, .distChip:hover, .togBtn:hover, .menuBtn:hover {
  background: linear-gradient(180deg, #a6b39f 0 50%, #869a7e 50% 100%);
}
.primaryBtn { background: linear-gradient(180deg, #6cae3f 0 50%, #4f8a2c 50% 100%); }
.primaryBtn:hover { background: linear-gradient(180deg, #79bd4a 0 50%, #589631 50% 100%); }
.primaryBtn:active, .secondaryBtn:active, .distChip:active, .togBtn:active, .menuBtn:active {
  box-shadow: inset -1px -1px 0 rgba(255,255,255,0.4), inset 1px 1px 0 rgba(0,0,0,0.5);
  transform: translateY(1px);
}
.distChip.sel, .togBtn.on { background: linear-gradient(180deg, #6cae3f 0 50%, #4f8a2c 50% 100%); }
.distChip.sel:hover, .togBtn.on:hover { background: linear-gradient(180deg, #79bd4a 0 50%, #589631 50% 100%); }
.primaryBtn .adBadge { margin-right: 7px; }

/* Game menu: centered stack of full-width MC buttons */
.menuBox { width: min(86vw, 340px); padding: 14px 16px; }
.menuBtns { display: flex; flex-direction: column; gap: 6px; }
.menuHint { margin-top: 12px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,0.14);
  font-size: 12px; line-height: 1.4; color: #ffd77a; text-align: center; }
.menuBtn { width: 100%; padding: 9px 0; font-size: 14px; }
/* menu button with a small item icon on the right (Mods = emerald) */
.menuBtnIcon { display: flex; align-items: center; justify-content: center; gap: 8px; }
.menuBtnIcon img { width: 18px; height: 18px; image-rendering: pixelated; pointer-events: none; }

/* ---------- Mods screen ---------- */
/* ---------- Mods screen ----------
   The panel is WIDE and the height is what is scarce: the game is landscape-only, so a
   phone gives ~850x400 and the binding limit is the ~315px left after the title and the
   button row. Hence four short columns rather than three tall ones - seven mods land in
   two rows with room to breathe. The grid scrolls, not the panel, so the title and OK
   stay put once the list outgrows the screen. */
.modsBox {
  width: min(94vw, 1100px);
  max-width: none;
  display: flex; flex-direction: column; min-height: 0;
  position: relative;   /* anchors the detail screen's corner Back button */
}
/* detail screen: Back lives in the TOP-LEFT corner (owner), which frees the bottom row
   to hold ONLY the buy/toggle control, properly centred - .invButtons already centres
   its content, it just used to have two children */
#modInfoModal .btnBack {
  position: absolute; left: 10px; top: 10px;
  z-index: 1;
  padding: 6px 14px;
}
#modInfoModal .modalTitle { text-align: center; }
#modsList {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 6px; margin-top: 4px;
  overflow-y: auto; min-height: 0; flex: 1 1 auto;
  align-content: start;
}
.modCard {
  display: flex; flex-direction: column; gap: 5px;
  border: 2px solid #000;
  background: rgba(0, 0, 0, 0.25);
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.15), inset -1px -1px 0 rgba(0,0,0,0.4);
  padding: 8px 9px;
  text-align: left;
}
.modCardOpens { cursor: pointer; }
.modCardOpens:hover { background: rgba(255, 255, 255, 0.07); }
.modCardTop { display: flex; align-items: center; gap: 8px; }
.modCardTop .modName { min-width: 0; overflow-wrap: anywhere; }
/* the blurb is clamped so every card in a row is the same height whatever the language
   runs to; the full text lives on the detail screen */
.modCardDesc {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}
/* "?" on the left, the action (toggle/price) PINNED RIGHT at exactly 3x the "?"'s
   width (owner) - fixed widths, not flex ratios: flex-grow distributes leftover space,
   so the visual ratio drifted with the card width */
.modCardActions { display: flex; align-items: center; gap: 6px; margin-top: auto; }
.modCardActions .togBtn, .modCardActions .modBuy { flex: 0 0 90px; min-width: 0; order: 1; margin-left: auto; }
.modCardActions .modMore { order: 0; }
/* the info button carries a short word now ("Info"); width follows the label */
.modMore {
  flex: 0 0 auto;
  border: 2px solid #000; border-radius: 0;
  padding: 7px 8px; font: inherit; font-size: 12px; font-weight: bold;
  color: #fff; cursor: pointer;
  background: linear-gradient(180deg, #6a6a6a 0 50%, #4a4a4a 50% 100%);
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.35), inset -1px -1px 0 rgba(0,0,0,0.5);
}
.modMore:hover { background: linear-gradient(180deg, #7a7a7a 0 50%, #575757 50% 100%); }

/* ---------- one mod in detail ----------
   Description first, screenshots UNDER it at full width. They were beside the text in a
   ~355px column, which is where a 16:9 frame reads worst; across the panel it actually
   shows something (owner). The whole body is one scroller, and both halves are capped to
   a readable measure - the panel goes to 1100px and neither a line of prose nor a 720px
   screenshot wants to be that wide. */
#modInfoBody {
  margin-top: 4px; min-height: 0; flex: 1 1 auto; overflow-y: auto;
}
#modInfoText, #modInfoMedia { max-width: 760px; margin: 0 auto; }
#modInfoText { font-size: 12px; line-height: 1.5; text-align: left; }
#modInfoText .modInfoLine { margin: 0 0 7px; padding-left: 12px; position: relative; }
#modInfoText .modInfoLine::before { content: "\2022"; position: absolute; left: 0; color: #8fd66a; }
/* a screenshot pinned to its paragraph, laid out like a figure in a document: indented
   with the text, a touch under half the measure so the page still reads as prose rather
   than as a slideshow, and with breathing room before the next paragraph */
#modInfoText .modFig {
  display: block; width: 60%; max-width: 420px; height: auto;
  margin: 2px 0 12px 12px;
  border: 2px solid #000; background: #000;
}
/* TWO per row (owner): at half width a 16:9 shot lands near its native 720 on a 2x
   phone screen, and a mod with five of them is one scroll instead of five */
#modInfoMedia { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 4px 0 2px; align-items: start; }
#modInfoMedia:empty { display: none; }
/* NOT pixelated: these are downscaled screenshots, not 16x16 tiles - crisp scaling
   would alias the voxel texture edges into noise. Capped at their native 720 so a wide
   panel does not blow them up into mush. */
#modInfoMedia img {
  width: 100%; height: auto;
  border: 2px solid #000; background: #000;
}
#modInfoAction { display: inline-flex; }
#modInfoAction .togBtn, #modInfoAction .modBuy { min-width: 96px; }

.modRow {
  display: flex; align-items: center; gap: 10px;
  border: 2px solid #000;
  background: rgba(0, 0, 0, 0.25);
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.15), inset -1px -1px 0 rgba(0,0,0,0.4);
  padding: 8px 10px;
  text-align: left;
}
.modIcon { width: 26px; height: 26px; image-rendering: pixelated; flex: 0 0 auto; }
.modInfo { flex: 1 1 auto; min-width: 0; }
.modName { font-size: 13px; font-weight: bold; }
.modDesc { font-size: 11px; color: #c8c8c8; }
.modRow .togBtn { flex: 0 0 auto; min-width: 56px; }
/* buy button: the mod's localized IAP price (green, like the confirm buttons) */
.modBuy {
  flex: 0 0 auto; min-width: 56px;
  border: 2px solid #000; border-radius: 0;
  padding: 7px 10px; font: inherit; font-size: 12px; font-weight: bold;
  color: #fff; cursor: pointer;
  background: linear-gradient(180deg, #6cae3f 0 50%, #4f8a2c 50% 100%);
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.5), inset -1px -1px 0 rgba(0,0,0,0.5);
  text-shadow: 1px 1px 0 rgba(0,0,0,0.6);
  image-rendering: pixelated;
}
.modBuy:hover { background: linear-gradient(180deg, #79bd4a 0 50%, #589631 50% 100%); }
.modBuy:disabled { opacity: 0.6; cursor: default; }

/* ---------- Minimap (mod) ---------- */
#minimap {
  position: absolute; right: 12px; top: 64px;   /* below the top-right inventory button */
  width: 129px; height: 129px;
  border: 2px solid #000;
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(0,0,0,0.45);
  image-rendering: pixelated;
  pointer-events: none;
  z-index: 9;
  opacity: 0.92;
}
#minimap.hidden { display: none; }
body.mobile #minimap { width: 104px; height: 104px; top: 12px; }
/* The phone screen is short: under the inventory button (top 64, like desktop) the map
   ate the little height there is. In the CORNER it fits - and the inventory button
   slides out of its way only WHILE the map is on (body.mapOn, toggled by tickMinimap):
   map off = the button back in its corner. 124 = 12 + 104 map + 8 gap; the no-ads
   button keeps its 52px offset left of the inventory button. */
body.mobile.mapOn #btnInv { right: 124px; }
body.mobile.mapOn #btnNoAds { right: 176px; }
body.mobile.mapOn #btnCam { right: 228px; }

/* ---------- Recipe strip + book (survival crafting) ---------- */
/* ONE horizontal row to the right of the craft area, inside #survTopRow, so it never
   grows the panel HEIGHT (owner: the old wrapping strip below the grid ballooned the
   whole inventory). flex:1 + min-width:0 lets it eat the remaining top-row width; the
   row itself is a single non-wrapping line scrolled by the < > buttons. */
#recipeStripWrap { display: flex; flex-direction: column; min-width: 0; }
#recipeStripWrap.hidden { display: none; }
/* the caption is free in the SURVIVAL inventory (the armor column already reserves more
   height than the craft column uses) and costs a line at the crafting TABLE, which is
   paid back by dropping the "Inventory" label's top margin there - see below. */
#recipeStripLabel { margin: 0 0 2px; }
#recipeStrip {
  min-width: 0;
  display: flex; align-items: center; gap: 2px;
}
#recipeStripRow {
  /* A WINDOW of --strip-cells cells: that many suggestions are visible and the rest is
     what the < > arrows scroll to. 6 at the crafting table, 5 in the pocket inventory
     (owner: less room there) - see #survInvWrap.pocketCraft below.

     A DEFINITE width, not flex-basis. A shrinkable flex item still contributes its
     MAX-CONTENT width when an ancestor sizes to content, so .invBox grew to fit the whole
     hidden recipe list even though the row was clipped (owner: "the UI expands to the size
     of the hidden list"). A definite width caps that contribution at the window. */
  --strip-cells: 6;
  width: calc(var(--strip-cells) * var(--slot) + (var(--strip-cells) - 1) * 3px);
  flex: 0 0 auto;
  min-width: 0;
  display: flex; flex-wrap: nowrap; gap: 3px;
  overflow-x: hidden;   /* scrolled by the arrows, no visible scrollbar */
  scroll-behavior: smooth;
}
/* the 2x2 pocket grid shares its row with the armor and offhand columns, so it gets one
   cell less than the crafting table (owner). STRIP_VISIBLE in screens.js mirrors this. */
#survInvWrap.pocketCraft #recipeStripRow { --strip-cells: 5; }
/* crafting table (not .pocketCraft): the craft column has no slack, so the caption's line
   is reclaimed from the "Inventory" label above the 9-wide grid - panel height unchanged */
#survInvWrap:not(.pocketCraft) #survInvLabel { margin-top: 0; }
#recipeStripRow .rbCell { flex: 0 0 auto; }
.recipeArrow {
  flex: 0 0 auto; width: 22px; align-self: stretch;
  background: var(--mc-btn); color: #fff;
  border: 2px solid #000; border-radius: 0; cursor: pointer;
  font-size: 16px; line-height: 1; padding: 0;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.4), inset -2px -2px 0 rgba(0,0,0,0.5);
}
.recipeArrow.hidden { display: none; }
.rbCell { cursor: pointer; }
.rbCell:hover { outline: 2px solid #6d28d9; }
.rbCell.rbGrey img { opacity: 0.53; }   /* 1.5x less transparent (owner) */
.rbCell.rbGrey .countPill { opacity: 0.75; }
/* translucent preview of an out-of-reach recipe inside the real grid cells */
.ghostIcon {
  position: absolute; left: var(--pad); top: var(--pad);
  width: var(--icon); height: var(--icon);
  opacity: 0.53; image-rendering: pixelated; pointer-events: none;   /* 1.5x less transparent (owner) */
}
.ghostPill { opacity: 0.75; }
#btnRecipeBook {
  width: 44px; height: 44px; margin-left: 6px;
  background: var(--mc-btn); border: 2px solid #000; border-radius: 0;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.4), inset -2px -2px 0 rgba(0,0,0,0.5);
  color: #fff; cursor: pointer; align-self: center;
}
#btnRecipeBook.hidden { display: none; }
#btnRecipeBook svg { width: 60%; height: 60%; pointer-events: none; }
/* the book: a second panel to the LEFT of the inventory box (owner mockup) */
#recipeBook {
  position: fixed; left: 10px; top: 5vh; bottom: 5vh;
  width: min(400px, 42vw);
  background: var(--mc-panel);
  border: 2px solid #000;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.55), inset -2px -2px 0 rgba(0,0,0,0.35), 0 4px 14px rgba(0,0,0,0.5);
  padding: 12px;
  display: flex; flex-direction: column; gap: 8px;
  z-index: 40;
  color: var(--mc-text-dark);
}
#recipeBook.hidden { display: none; }
#rbTitle { font-size: 18px; font-weight: bold; }
#rbFilter {
  font: inherit; font-size: 13px; padding: 6px 8px;
  background: var(--mc-btn); color: #fff;
  border: 2px solid #000; border-radius: 0; cursor: pointer;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.4), inset -2px -2px 0 rgba(0,0,0,0.5);
}
#rbSearch {
  font: inherit; font-size: 13px; padding: 6px 8px;
  border: 2px solid #000; border-radius: 0;
  background: #dedede; color: #222;
}
#rbGrid {
  flex: 1; overflow-y: auto;
  display: flex; flex-wrap: wrap; gap: 3px; align-content: flex-start;
}
body.mobile #recipeBook { width: min(320px, 46vw); top: 2vh; bottom: 2vh; padding: 8px; }

/* ---------- Inventory (light vanilla panel) ---------- */

.invBox {
  /* vanilla panel: hugs the 9-column grid, tab tongues stick out above/below */
  position: relative;
  width: fit-content;
  max-width: 96vw;
  height: min(78vh, 620px);    /* creative: constant tall panel; the grid eats the space */
  max-height: none;
  overflow: visible;           /* tab tongues extend past the panel edge */
  display: flex;
  flex-direction: column;      /* grid grows; compact footer pinned below */
  text-align: left;
  background: var(--mc-panel);
  border-color: #000;
  box-shadow: inset 3px 3px 0 #fff, inset -3px -3px 0 #555;
}
.invBox.survMode { height: auto; }   /* survival inventory: compact vanilla panel */
.invBox .modalTitle, .lightBox .modalTitle, .invSectionLabel {
  color: var(--mc-text-dark);
  text-shadow: none;
  font-weight: normal;
  font-size: 15px;
  text-align: left;
  letter-spacing: 0.5px;
  margin: 0;
}
.invSectionLabel { margin: 8px 0 2px; }
#furnaceTitle { text-align: center; }
#invTitleRow { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; min-height: 22px; }
.invBox #invHint { color: #5a5a5a; text-shadow: 1px 1px 0 rgba(255,255,255,0.35); }
.invBox .invButtons { margin-top: 6px; }

/* creative tab tongues: anchored OUTSIDE the panel; the active one is taller and
   overlaps the panel border so its face merges with the panel (vanilla look) */
#invTabs, #invTabsBottom {
  position: absolute; left: -2px; right: -2px;
  display: flex; gap: 2px;
  z-index: 3;
}
#invTabs { top: -42px; height: 48px; align-items: flex-start; }
#invTabsBottom { bottom: -42px; height: 48px; align-items: flex-end; }
.invTab {
  height: 40px;
  cursor: pointer; touch-action: manipulation;
  background: var(--mc-panel);
  border: 2px solid #000;
  font: inherit;
  box-sizing: border-box;
}
.invTab.active { height: 48px; }
#invTabs .invTab {
  border-bottom: none;
  box-shadow: inset 2px 2px 0 #fff, inset -2px 0 0 #555;
}
#invTabsBottom .invTab {
  border-top: none;
  box-shadow: inset 2px 0 0 #fff, inset -2px -2px 0 #555;
}

#invGrid {
  display: grid;
  grid-template-columns: repeat(9, var(--slot));  /* vanilla creative: 9 columns */
  grid-auto-rows: var(--slot);
  width: calc(9 * var(--slot) + 16px);   /* 9 slots + the 16px scrollbar column */
  gap: 0;                    /* slots touch; their bevels draw the grid lines */
  margin: 6px 0;
  padding: 0;
  flex: 1 1 auto;            /* grow to fill the panel; the footer stays compact */
  min-height: 0;
  overflow-y: scroll;        /* always-visible MC scrollbar column */
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  scrollbar-color: #c6c6c6 #565656;
}
#invGrid::-webkit-scrollbar { width: 16px; }
#invGrid::-webkit-scrollbar-track {
  background: var(--mc-slot);
  box-shadow: inset 2px 2px 0 var(--mc-shadow), inset -2px -2px 0 #fff;
}
#invGrid::-webkit-scrollbar-thumb {
  background: var(--mc-panel);
  border: 2px solid #000;
  box-shadow: inset 2px 2px 0 #fff, inset -2px -2px 0 #555;
}
.invCell {
  position: relative;
  width: var(--slot); height: var(--slot);
  background: var(--mc-slot);
  border: 0;
  border-radius: 0;
  box-shadow: inset 2px 2px 0 var(--mc-shadow), inset -2px -2px 0 #fff;
  cursor: pointer;
  box-sizing: border-box;
}
/* hover highlight for a MOUSE only. On touch there is no real hover: the last
   tapped cell keeps the :hover state, which looked like a second, greyish
   selection competing with the green outline (owner: remove the grey thing). */
@media (hover: hover) {
  .invCell:hover::after {
    content: ""; position: absolute; inset: 2px;
    background: rgba(255,255,255,0.45);
    pointer-events: none;
  }
}
.invCell img {
  position: absolute; left: var(--pad); top: var(--pad);
  width: var(--icon); height: var(--icon);
  image-rendering: pixelated;
  pointer-events: none;
}
.invCell.sel { box-shadow: inset 0 0 0 2px #fff, inset 2px 2px 0 var(--mc-shadow); }
.invCell.pick { box-shadow: inset 0 0 0 2px #ffd24a, inset 2px 2px 0 var(--mc-shadow); }

#invName {
  min-height: 15px; font-size: 13px; color: var(--mc-text-dark);
  text-shadow: 1px 1px 0 rgba(255,255,255,0.4);
  margin-bottom: 2px; font-weight: bold;
}

/* the selected stack on TOUCH stays in its cell with an outline instead of
   riding under the finger as a ghost (owner) */
.survSlot.selCell, .hbSlot.selCell, .invCell.selCell {
  outline: 3px solid #6fe36f;
  outline-offset: -3px;
}

/* after the slider is released the chosen amount stays visible as a thin green
   bar across the top of the cell - no numbers, just the proportion */
.miniSplit {
  position: absolute; left: 2px; right: 2px; top: 2px; height: 3px;
  background: rgba(0,0,0,0.55);
  pointer-events: none;
}
.miniSplit > i { display: block; height: 100%; background: var(--mc-green); }

/* ---------- touch: split-amount slider (long press on a stack) ---------- */
#splitSlider {
  position: fixed; left: 0; top: 0;
  z-index: 90;                        /* above the carry ghost (80) */
  width: 132px; padding: 6px 8px 8px;
  background: rgba(20,20,20,0.94);
  border: 2px solid #000;
  box-shadow: 0 3px 10px rgba(0,0,0,0.55);
  touch-action: none;                 /* the knob is dragged, never scrolled */
  user-select: none; -webkit-user-select: none;
}
#splitSlider.hidden { display: none; }
#splitLabel {
  text-align: center; font-size: 13px; margin-bottom: 5px; color: #fff;
  text-shadow: 2px 2px 0 rgba(0,0,0,0.55);
}
#splitTrack {
  position: relative; height: 12px;
  background: #1b1b1b; border: 2px solid #000;
  box-shadow: inset 2px 2px 0 rgba(0,0,0,0.6);
}
#splitFill { position: absolute; left: 0; top: 0; bottom: 0; width: 50%; background: var(--mc-green); }
#splitKnob {
  position: absolute; top: -4px; width: 10px; height: 18px; margin-left: -5px;
  background: #cfcfcf; border: 2px solid #000;
}

#invGhost {
  position: fixed; left: 0; top: 0;
  width: 30px; height: 30px;          /* match the in-cell icon size, not 2x */
  pointer-events: none;
  z-index: 80;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));
}
#invGhost.hidden { display: none; }
#invGhost img {
  width: 100%; height: 100%;
  image-rendering: pixelated;
}

#invHotbar {
  display: flex; gap: 0; justify-content: flex-start;
  padding: 0; width: fit-content; margin: 4px 0 0;
  background: none;
  box-shadow: none;
}
#invHotbar .hbSlot {
  width: var(--slot); height: var(--slot);
  background: var(--mc-slot);
  border: 0;
  box-shadow: inset 2px 2px 0 var(--mc-shadow), inset -2px -2px 0 #fff;
}
#invHotbar .hbSlot img { inset: var(--pad); width: var(--icon); height: var(--icon); }
#invHotbar .hbSlot.sel { box-shadow: inset 0 0 0 2px #fff, inset 2px 2px 0 var(--mc-shadow); }
#invHotbar .hbSlot.pick { box-shadow: inset 0 0 0 2px #ffd24a, inset 2px 2px 0 var(--mc-shadow); }
#invHotbar .hbSlot .num { color: rgba(40,40,40,0.7); text-shadow: none; }

#invHint { font-size: 11px; color: #5a5a5a; margin-top: 4px; }

/* ---------- Overlays ---------- */

#playOverlay {
  position: fixed; inset: 0;
  background: rgba(5,8,16,0.55);
  display: flex; align-items: center; justify-content: center;
  z-index: 40;
  cursor: pointer;
}
#playOverlay.hidden { display: none; }
#playLabel {
  background: var(--mc-panel-dark);
  border: 2px solid #000;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.4), inset -2px -2px 0 rgba(0,0,0,0.5);
  border-radius: 0;
  padding: 16px 30px;
  font-size: 20px;
}

#loading {
  position: fixed; inset: 0;
  background: #0b0e14;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  z-index: 60;
}
#loading.hidden { display: none; }
#loadTitle { font-size: 36px; font-weight: bold; letter-spacing: 3px; margin-bottom: 24px; }
#loadBarOuter {
  width: min(64vw, 320px); height: 16px;
  background: #1b1b1b; border: 2px solid #000; border-radius: 0;
  box-shadow: inset 2px 2px 0 rgba(0,0,0,0.6);
  overflow: hidden;
}
#loadBar { width: 0%; height: 100%; background: var(--mc-green); transition: width 0.15s; }
#loadLabel { margin-top: 12px; font-size: 14px; color: #aeaeae; }

/* ---------- Title screen (MC-style world list) ---------- */

#titleScreen {
  position: fixed; inset: 0;
  background: #0b0e14;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 18px;
  z-index: 58;               /* under #loading (60): picking a world reveals the loader */
  padding: 10px; box-sizing: border-box;
  overflow: hidden;          /* clips the oversized blurred backdrop below */
}
/* blurred scenery backdrop: `cover` scales the image UP on whichever axis is
   short, so the whole iframe is always filled - never letterbox bars. The
   slight scale() pushes the blur's soft transparent fringe off-screen.
   z-index -1 = behind the content but still above the panel background
   (#titleScreen is a stacking context via its own z-index). */
#titleScreen::before {
  content: "";
  position: absolute; inset: 0;
  background: url("assets/tex/title_bg.jpg") center / cover no-repeat;
  filter: blur(7px) brightness(0.72);
  transform: scale(1.06);
  z-index: -1;
}
#titleScreen.hidden { display: none; }
#titleLogo { font-size: 36px; font-weight: bold; letter-spacing: 3px; }
#worldsPanel {
  background: var(--mc-panel-dark);
  border: 2px solid #000;
  box-shadow: inset 2px 2px 0 rgba(255,255,255,0.22), inset -2px -2px 0 rgba(0,0,0,0.5);
  padding: 12px 14px;
  width: min(92vw, 420px);
  box-sizing: border-box;
  display: flex; flex-direction: column;
  max-height: 70vh; max-height: 70dvh;
}
#worldsPanel .modalTitle { text-align: center; }
#worldsEmpty { color: #b8b8b8; font-size: 13px; text-align: center; margin: 10px 0; }
#worldsEmpty.hidden { display: none; }
#worldList { overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 6px; }
.worldRow {
  display: flex; align-items: center; gap: 8px;
  border: 2px solid #000;
  background: linear-gradient(180deg, #6f6f6f 0 50%, #5a5a5a 50% 100%);
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.35), inset -1px -1px 0 rgba(0,0,0,0.5);
  padding: 8px 10px;
  cursor: pointer;
}
.worldRow:hover { background: linear-gradient(180deg, #a6b39f 0 50%, #869a7e 50% 100%); }
.worldInfo { flex: 1 1 auto; min-width: 0; text-align: left; }
.worldName { font-size: 14px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.worldSub { font-size: 11px; color: #d8d8d8; }
.worldDel {
  flex: 0 0 auto;
  border: 2px solid #000; border-radius: 0;
  padding: 4px 8px; font: inherit; font-size: 11px; font-weight: bold;
  color: #fff; cursor: pointer;
  background: linear-gradient(180deg, #8c8c8c 0 50%, #6f6f6f 50% 100%);
  box-shadow: inset 1px 1px 0 rgba(255,255,255,0.5), inset -1px -1px 0 rgba(0,0,0,0.5);
  text-shadow: 1px 1px 0 rgba(0,0,0,0.6);
}
.worldDel.arm { background: linear-gradient(180deg, #c0453a 0 50%, #99352c 50% 100%); }
#worldNameInput {
  flex: 1 1 auto; min-width: 60px; box-sizing: border-box;
  padding: 5px 8px; font: inherit; font-size: 13px;
  background: #1b1b1b; color: #fff;
  border: 2px solid #000; border-radius: 0;
  box-shadow: inset 1px 1px 0 rgba(0,0,0,0.5);
}
#worldModeChips { display: flex; gap: 6px; }
/* the create-world dialog opens OVER the title screen: generic .modal z-index
   is 50, the title sits at 58 - without this the dialog opened invisibly
   underneath it ("Create New World does nothing", owner bug) */
#createModal { z-index: 62; }

#toast {
  position: fixed; left: 50%; bottom: 80px;
  transform: translateX(-50%);
  background: var(--mc-tip-bg);
  border: 2px solid var(--mc-tip-bd);
  border-radius: 0;
  padding: 9px 16px;
  font-size: 15px;
  z-index: 70;
  pointer-events: none;
  transition: opacity 0.3s;
}
#toast.hidden { opacity: 0; }

/* ---------- Survival mode ---------- */

/* Two columns sitting directly over the hotbar, vanilla-style: health (with
   armor above it) hugs the LEFT edge, hunger (with air above it) the RIGHT.
   The width is pinned to the hotbar's own width so the ends line up with it -
   10 slots is what #hotbar lays out, hence the same slot var and padding. */
#survHud {
  position: absolute; left: 50%;
  /* sit ABOVE the hotbar on every screen size. The hotbar is --hb-slot tall
     plus ~14px of padding+border and sits ~6-10px off the bottom; a fixed 54/62
     was fine on desktop but the shorter mobile/narrow hotbars still poked
     through it (owner: the bars sit under the hotbar on mobile). Deriving the
     offset from --hb-slot keeps the gap correct at 42 / 40 / 34 px slots. */
  bottom: calc(var(--hb-slot) + 26px);
  transform: translateX(-50%);
  width: calc(var(--hb-slot) * 9 + 10px);
  display: flex; justify-content: space-between; align-items: flex-end;
  pointer-events: none;
  z-index: 9;
}
#survHud.hidden { display: none; }
.statSide { display: flex; flex-direction: column; gap: 2px; }
#statRight .statRow { justify-content: flex-end; }
.statRow { display: flex; gap: 1px; justify-content: flex-start; }
.statRow.hidden { display: none; }
.statIco { width: 16px; height: 16px; display: inline-block; }
.statIco svg { width: 100%; height: 100%; filter: drop-shadow(1px 1px 0 rgba(0,0,0,0.6)); }
body.mobile .statIco { width: 13px; height: 13px; }

/* worn carved pumpkin: vanilla's pumpkinblur over the whole viewport. The PNG is
   a square vignette with a carved-face cutout, stretched to fill (vanilla does the
   same), so most of the view is blocked - that IS the trade-off for the disguise.
   NOTE: `.hidden` is styled PER ELEMENT in this file - a new hideable element
   needs its own display:none rule or the class does nothing. */
#pumpkinOverlay {
  position: fixed; inset: 0;
  background-image: url("assets/tex/ui_pumpkinblur.png");
  background-size: 100% 100%;
  image-rendering: pixelated;
  pointer-events: none;
  /* over the world, but UNDER the hotbar/HUD (10) and every modal (50) - vanilla
     blurs the view, not the interface. */
  z-index: 4;
}
#pumpkinOverlay.hidden { display: none; }

#hurtFlash {
  position: fixed; inset: 0;
  background: radial-gradient(ellipse at center, rgba(200,0,0,0.12) 40%, rgba(200,0,0,0.55) 100%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s;
  z-index: 60;
}

.durOuter {
  position: absolute; left: 4px; right: 4px; bottom: 3px;
  height: 3px;
  background: rgba(0,0,0,0.7);
  pointer-events: none;
}
.durIn { height: 100%; }

#survInvWrap { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin-top: 6px; }
#survInvWrap.hidden { display: none; }
#craftWrap { display: flex; align-items: center; gap: 12px; }
#craftGrid { display: grid; gap: 0; }
#craftArrow { font-size: 22px; color: #6f6f6f; }
#survGrid { display: grid; grid-template-columns: repeat(9, var(--slot)); gap: 0; }
.survSlot .countPill { right: 1px; bottom: 0; }
#invGhost .gcnt { right: -6px; bottom: -6px; }
#invTabs.hidden, #invTabsBottom.hidden, #invGrid.hidden { display: none; }
#inv, #furnaceModal, #chestModal { padding-right: 48px; box-sizing: border-box; }   /* room for the X close tab on the right edge */

#furnaceLayout {
  display: flex; align-items: center; justify-content: center; gap: 18px;
  margin: 14px 0;
}
.furnCol { display: flex; flex-direction: column; align-items: center; gap: 6px; }
#furnIn, #furnFuel { width: var(--slot); height: var(--slot); }
#furnIn .survSlot, #furnFuel .survSlot { width: var(--slot); height: var(--slot); }
/* vanilla furnace: the OUTPUT slot is the big one */
#furnOut { width: calc(var(--slot) + 12px); height: calc(var(--slot) + 12px); }
#furnOut .survSlot { width: calc(var(--slot) + 12px); height: calc(var(--slot) + 12px); }
#furnOut .survSlot img { left: calc(var(--pad) + 6px); top: calc(var(--pad) + 6px); }
#furnFlame { transition: opacity 0.3s; }
.furnBurnOuter, .furnProgOuter {
  width: var(--slot); height: 5px;
  background: var(--mc-slot);
  border: 0;
  box-shadow: inset 1px 1px 0 var(--mc-shadow), inset -1px -1px 0 #fff;
}
.furnProgOuter { width: 70px; }
#furnBurn { height: 100%; background: #e8842a; }
#furnProg { height: 100%; background: #fff; width: 0; }
.deathBox { border-color: #7a1010; }
#deathModal .modalBox { background: rgba(60, 8, 8, 0.92); }
/* give the death stack a real width so the reused mod row (icon + name/desc +
   toggle/buy) sits under Respawn at a sensible size */
#deathModal .menuBtns { width: min(84vw, 320px); }
#deathModRow:empty { display: none; }   /* case 2 (mod enabled): no gap left behind */

/* shared 9-column slot grid (furnace/chest player inventory) */
.slotGrid9 { display: grid; grid-template-columns: repeat(9, var(--slot)); gap: 0; justify-content: flex-start; margin-top: 4px; }
#furnPlayerHotbar { margin-top: 8px; }
#chestGrid { margin-bottom: 2px; }
/* the scrollable content of the furnace/chest; the X tab lives OUTSIDE it so it
   does not scroll away (owner: chest scroll was broken by overflow on lightBox) */
.lightScroll { overflow-y: auto; max-height: 86vh; max-height: 86dvh; -webkit-overflow-scrolling: touch; min-height: 0; }

/* sleep overlay: slow fade to black while lying down; the wake-up button stays clickable */
#sleepOverlay {
  position: fixed; inset: 0; z-index: 60;
  background: #000; opacity: 0;
  transition: opacity 2.8s ease-in;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  padding-bottom: 12vh; gap: 14px;
}
#sleepOverlay.asleep { opacity: 1; }
#sleepOverlay.hidden { display: none; }
/* while asleep the wake-up button owns the bottom-centre: push toasts (e.g.
   "respawn point set") above it instead of covering it */
#sleepOverlay:not(.hidden) ~ #toast { bottom: calc(12vh + 110px); }
#sleepLabel { color: #cfcfcf; font-size: 15px; text-shadow: 0 2px 0 #000; }
#sleepOverlay .menuBtn { max-width: 220px; }

/* light panel variant for the furnace/chest screens (matches the inventory look) */
.lightBox {
  position: relative;          /* anchor the absolute X close tab */
  overflow: visible;           /* let the X tab stick out past the right edge */
  max-height: 88vh; max-height: 88dvh;
  background: var(--mc-panel);
  border-color: #000;
  box-shadow: inset 3px 3px 0 #fff, inset -3px -3px 0 #555;
  text-align: left;
}

/* creative inventory search: inline with the "Search Items" title (vanilla) */
#invSearch {
  flex: 1 1 auto; min-width: 60px; box-sizing: border-box; margin: 0;
  padding: 3px 6px; font: inherit; font-size: 13px;
  background: #fff; color: #222;
  border: 2px solid #000; border-radius: 0;
  box-shadow: inset 1px 1px 0 rgba(0,0,0,0.25);
}
#invSearch.hidden { display: none; }

/* creative tab icons */
.invTab.iconTab {
  width: 56px; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
.invTab.iconTab img { width: 30px; height: 30px; image-rendering: pixelated; pointer-events: none; }
.invTab.tabRight { margin-left: auto; }
/* close = an X tab on the right edge (mirrors the category tabs on top/bottom).
   Outside the panel like the other tabs; the panel is nudged left on mobile so
   it fits (see the media block). */
.tabClose {
  position: absolute; right: -40px; top: 0;
  width: 40px; height: 44px;
  font-size: 20px; font-weight: bold; color: #333; line-height: 1;
  border-left: none;
  box-shadow: inset -2px 2px 0 #fff, inset 2px 0 0 #555;
}
.tabClose:active { background: var(--mc-btn-hi); }

/* crafting-table view hides the survival-inventory-only trims */
#armorCol.hidden, #shieldCol.hidden, #survInvLabel.hidden { display: none; }

/* survival inventory top row: LEFT equipment column (armor + offhand), RIGHT craft column
   (craft area on top, recipe strip at the bottom). align-items:stretch makes the right
   column as tall as the equipment column so space-between can drop the strip to the
   offhand's row. */
#survTopRow { display: flex; gap: 8px; align-items: stretch; justify-content: flex-start; margin-bottom: 0; }
#armorCol, #shieldCol { display: flex; flex-direction: column; gap: 0; }
/* right column: craft on top, [offhand][strip] pinned to the bottom. space-between drops
   that row level with the BOOTS cell, which is what puts the offhand beside the boots. */
#craftCol { display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between; min-width: 0; }
/* flex-END, not center: the strip wrapper is taller than the offhand (it carries the
   "Recipes" caption), and centring floated the offhand above the row. Aligning bottoms
   puts the offhand cell on exactly the same band as the strip cells - and as the boots,
   since space-between pins this row's bottom to the armor column's bottom (owner). */
#craftBottomRow { display: flex; align-items: flex-end; gap: 8px; min-width: 0; }
/* only the PLACEHOLDER silhouette is dimmed/small; a real equipped item keeps
   the normal .invCell img size */
.armorSlot img.ph { opacity: 0.45; width: 60% !important; height: 60% !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%); }

/* ---------- compact profile for phones ----------
   Triggers on narrow screens (portrait) OR LOW screens (landscape phones - the
   old width-only query never fired there, so a 390px-tall viewport got the full
   desktop panel: cut-off OK buttons, off-screen tabs, huge spacings). Smaller
   slots via the --slot/--icon/--pad vars + every vertical spacing tightened. */
@media (max-width: 560px), (max-height: 480px) {
  /* Slot size adapts to BOTH axes so the whole inventory fits without a scroll
     (owner: make the slots smaller until it fits). Width limit: 9 across the
     viewport. Height limit: the survival inventory is ~8 slot-rows tall (4 armour
     + 3 grid + 1 hotbar) plus ~175px of chrome (the OK button and its row are
     gone now, see p3). Width subtracts 40 for panel padding + the narrow X tab -
     the X steals width, and PORTRAIT is width-bound, so it is kept slim.
     clamp keeps it readable (>=26) and caps at 50. overflow-y:auto is only a fallback
     for the rare case the estimate is short. */
  /* One height-driven slot size for the whole inventory: it scales with viewport
     HEIGHT (we are landscape-only) and keeps the same proportions at every
     resolution (owner p4). 8 rows is the tallest of the creative / survival
     panels; the width term is only a safety cap so 9 columns + the X tab fit.
     Chrome is now small (titles shrank, p1) so the slots grow to fill (p2). */
  :root {
    --slot: clamp(28px, min(calc((100vw - 46px) / 9), calc((100dvh - 104px) / 8)), 60px);
    --icon: calc(var(--slot) - 6px);
    --pad: 3px;
  }

  /* panel chrome: thin padding, tight title, no hint line */
  .modalBox { padding: 3px 8px; max-height: calc(100vh - 8px); max-height: calc(100dvh - 8px); }
  /* mods on a landscape phone: the height is the scarce axis, so everything vertical
     tightens and the blurb clamps to two lines. Columns get narrower too, which keeps
     four of them on an ~850px screen. */
  #modsList { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 4px; }
  .modCard { padding: 5px 6px; gap: 3px; }
  .modCard .modIcon { width: 20px; height: 20px; }
  .modCard .modName { font-size: 12px; }
  .modCardDesc { -webkit-line-clamp: 2; font-size: 10px; }
  .modCardActions .togBtn, .modCardActions .modBuy { padding: 5px 6px; font-size: 11px; flex-basis: 78px; }
  .modMore { padding: 5px 6px; font-size: 11px; }
  #modInfoText { font-size: 11px; line-height: 1.45; }
  /* landscape phone: the panel is ~800 wide, so 60% is already near the shot's native
     720 on a 2x screen - keep it, just tighten the gaps */
  #modInfoText .modFig { margin-bottom: 8px; }
  #modInfoText .modInfoLine { margin-bottom: 5px; }
  .modalTitle { font-size: 11px; margin-bottom: 0; line-height: 1; }
  .invBox .modalTitle, .lightBox .modalTitle, .invSectionLabel { font-size: 11px; line-height: 1; }
  #invTitleRow { min-height: 0; gap: 6px; }
  .invSectionLabel { margin: 1px 0 0; }
  #invGrid { margin: 0; width: calc(9 * var(--slot)); }   /* no scrollbar column on touch (overlay) */
  #invName { min-height: 6px; font-size: 9px; margin-bottom: 0; line-height: 1; }
  #invHint { display: none; }            /* the "pick a block" hint eats a line */
  #invHotbar { margin: 1px 0 0; }
  .invButtons { margin-top: 4px; }
  .invBox .invButtons { margin-top: 4px; }
  .primaryBtn, .secondaryBtn { padding: 5px 12px; font-size: 12px; }

  /* creative panel: fill nearly the whole height so the slots stretch to match
     the survival panel (owner p2). Only the tab tongues (~28px each edge) are
     reserved above/below. */
  .invBox { height: calc(100vh - 64px); height: calc(100dvh - 64px); }
  /* p3: the X close tab lives just past the right edge, so shift the panel left
     to keep it on-screen, and shrink it to match the smaller category tabs */
  #inv, #furnaceModal, #chestModal { padding-right: 30px; box-sizing: border-box; }
  .tabClose { right: -30px; width: 30px; height: 34px; font-size: 15px; top: 0; }
  /* survival-inventory / crafting views: auto height capped to the screen;
     overflow scrolls INSIDE #survInvWrap (the tongues sit outside the panel
     and must not be clipped by an overflow on the panel itself) */
  /* survival inventory: no scroll (owner p3). The 8-row layout fits because the
     titles shrank and the slot is sized from an 8-row budget. */
  .invBox.survMode { height: auto; max-height: calc(100vh - 60px); max-height: calc(100dvh - 60px); }
  .invBox.survMode.noTabs { max-height: calc(100vh - 10px); max-height: calc(100dvh - 10px); }   /* no tongues -> full height */
  #survInvWrap { gap: 3px; margin-top: 2px; overflow: visible; min-height: 0; }
  #survTopRow { gap: 6px; }
  #craftWrap { gap: 8px; }

  /* furnace / chest: no scroll (owner p3). A double chest is 10 slot-rows, so
     the chest gets its own 10-row slot size; the furnace keeps the base slot. */
  .lightScroll { overflow: visible; max-height: none; }
  .lightBox { max-height: calc(100vh - 8px); max-height: calc(100dvh - 8px); }
  #chestModal {
    --slot: clamp(20px, min(calc((100vw - 46px) / 9), calc((100dvh - 74px) / 10)), 60px);
    --icon: calc(var(--slot) - 6px);
  }
  #furnaceLayout { margin: 4px 0; gap: 12px; }
  .furnCol { gap: 3px; }
  .slotGrid9 { margin-top: 2px; }
  #furnPlayerHotbar { margin-top: 4px; }

  .invTab { height: 26px; }
  .invTab.active { height: 30px; }
  #invTabs { top: -28px; height: 32px; }
  #invTabsBottom { bottom: -28px; height: 32px; }
  .invTab.iconTab { width: 38px; }
  .invTab.iconTab img { width: 20px; height: 20px; }
}
