/* ============================================================
   Castaway Cove — UI styles
   "Cozy island" theme (Heartopia / Animal Crossing / Stardew):
   warm parchment + soft wood, rounded shapes, gentle shadows,
   nature pastels. Handcrafted & inviting — no metal, no clutter.
   All UI is DOM/CSS overlaid on the game <canvas>.
   ============================================================ */

:root {
  /* parchment & wood materials */
  --parch:     #fff8ea;            /* light parchment plate */
  --parch-2:   #fdefd2;            /* cream */
  --parch-3:   #f6e1b9;            /* deeper cream */
  --panel:     rgba(255, 248, 234, .95);
  --panel-2:   rgba(253, 239, 210, .95);
  --panel-3:   #f4e3bf;
  --paper:     #fff8ea;            /* legacy alias (panels) */
  --paper-2:   #fdefd2;

  /* warm wood frames & ink */
  --wood:      #d2a86e;
  --wood-mid:  #bb8a52;
  --wood-dark: #8a5f33;
  --ink:       #5e431f;            /* primary warm-brown text */
  --ink-soft:  #8a6b42;            /* secondary text */
  --cream:     #5e431f;            /* legacy alias → primary text */
  --cream-dim: #8a6b42;            /* legacy alias → secondary text */

  /* honey accents (headers, buttons, glow) */
  --gold:      #eaa53e;            /* honey accent */
  --gold-hi:   #c47a22;            /* deep honey — emphasis on light */
  --gold-mid:  #d6ad72;            /* soft wood border */
  --gold-lo:   #ecd3a3;            /* light tan divider */
  --sea-deep:  #c47a22;            /* legacy alias → honey headers */
  --sand-dark: #ecd3a3;
  --shadow:    rgba(120, 85, 45, .22);

  /* nature pastels for survival bars */
  --hp:    #f06d6d; --hp2:  #ff9a8d;     /* warm coral */
  --food:  #f0a83c; --food2:#ffca6b;     /* honey */
  --water: #4fb4dd; --water2:#86d6f0;    /* sky */
  --stamina: #8bc74a; --stamina2:#c3e88a; /* leaf-green */
  --xp:    #ffce5c;
  --good:  #7bc47f; --good2:#a4dba0;     /* leaf */
  --warn:  #f0a83c; --bad:  #f06d6d;
  --leaf:  #7bc47f; --leaf-dark:#5a9e5e; --coral:#f4977b; --sun:#ffce5c;

  --radius: 20px;
  --font: "Baloo 2", "Baloo Thai 2", "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
}

/* touch-action does NOT inherit — without this on every element, iOS Safari
   double-tap-zooms on rapid UI taps (ACTION spam) and the zoom sticks.
   `manipulation` still allows scroll/pan inside panels; the gesture-event
   blocker in Input.js covers pinch. */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }

html, body {
  margin: 0; padding: 0; width: 100%;
  height: 100vh; height: 100dvh; /* dvh tracks the mobile address bar so nothing hides under it */
  overflow: hidden; background: #bfe3ce; color: var(--ink);
  font-family: var(--font);
  user-select: none; -webkit-user-select: none; touch-action: none;
}

canvas#game { display: block; width: 100vw; height: 100vh; height: 100dvh; image-rendering: auto; }

.hidden { display: none !important; }
kbd {
  background: linear-gradient(#fff8ea, #f4e3bf); color: var(--wood-dark);
  border: 1.5px solid var(--wood); border-radius: 8px;
  padding: 1px 7px; font-size: .8em; font-weight: 800; font-family: var(--font);
  box-shadow: 0 2px 0 rgba(138,95,51,.3); display: inline-block;
}

/* ---------------- Buttons ---------------- */
.btn {
  font-family: var(--font); font-weight: 800; font-size: 1rem;
  border: none; border-radius: 999px; padding: 12px 26px; cursor: pointer;
  transition: transform .08s ease, filter .15s ease; color: #5e3c10;
}
.btn:active { transform: translateY(2px) scale(.98); }
.btn-primary {
  background: linear-gradient(#ffd271, #f5a73f); box-shadow: 0 5px 0 #c47a22;
  border: 2px solid #fff3d4; color: #6e4310;
}
.btn-primary:hover { filter: brightness(1.05); }
.btn-ghost { background: rgba(255,248,234,.7); color: var(--wood-dark); box-shadow: 0 5px 0 rgba(138,95,51,.25); border: 2px solid var(--wood); }
.icon-btn {
  background: linear-gradient(#fff8ea, #f4e3bf); color: var(--wood-dark);
  border: 2px solid var(--wood); width: 38px; height: 38px;
  border-radius: 50%; font-size: 1.1rem; font-weight: 800; cursor: pointer; box-shadow: 0 3px 0 rgba(138,95,51,.3);
}
.icon-btn:active { transform: translateY(2px); }

/* ---------------- Overlay screens ---------------- */
.overlay-screen {
  position: fixed; inset: 0; z-index: 50; display: flex;
  align-items: flex-start; justify-content: center; padding: 20px;
  background: radial-gradient(circle at 50% 30%, #bfe6d6, #8fcfe0 60%, #6cb6cf);
  overflow-y: auto; overflow-x: hidden;
}

#title-screen {
  background-image: linear-gradient(rgba(20,50,45,.45), rgba(20,50,45,.45)), url("../assets/background/menuHero.webp");
  background-size: cover; background-position: center;
  align-items: center; padding: 2vh 20px;
  /* short landscape phones (iPhone 11-class, ~390-414px tall) can't fit the
     whole column — was `overflow: hidden`, which just clipped the bottom
     buttons off-screen with no way to reach them. overflow-y: auto lets the
     card scroll into view instead; nothing above this height ever shows a
     scrollbar since the content fits and there's nothing to scroll. */
  overflow-y: auto; overflow-x: hidden;
}
/* narrow/portrait screens crop this wide image down to its center sand —
   shift toward the left edge so the shipwreck + sea stay in frame */
@media (max-width: 600px) { #title-screen { background-position: left center; } }

/* ---- cold-start preloader (sits above the title until sprites are ready) ---- */
#loading-screen {
  position: fixed; inset: 0; z-index: 60; display: flex;
  align-items: center; justify-content: center; padding: 20px;
  background: radial-gradient(circle at 50% 30%, #bfe6d6, #8fcfe0 60%, #6cb6cf);
  transition: opacity .45s ease;
}
#loading-screen.done { opacity: 0; pointer-events: none; }
.load-card { text-align: center; color: #fff8ea; max-width: 360px; width: 100%; }
.load-logo { font-size: 4rem; filter: drop-shadow(0 8px 0 rgba(120,85,45,.2)); animation: bob 3s ease-in-out infinite; }
.load-card h1 {
  font-size: clamp(1.8rem, 7vw, 3rem); margin: .1em 0 .9em; letter-spacing: 1px; color: #fff8ea;
  text-shadow: 0 4px 0 #c47a22, 0 6px 12px rgba(120,85,45,.35);
}
.load-bar {
  height: 14px; width: 100%; border-radius: 999px; overflow: hidden;
  background: rgba(120,85,45,.28); box-shadow: inset 0 2px 4px rgba(120,85,45,.3);
}
.load-fill {
  height: 100%; width: 0%; border-radius: 999px;
  background: linear-gradient(90deg, #ffce5c, #eaa53e);
  box-shadow: 0 0 8px rgba(234,165,62,.6); transition: width .2s ease;
}
.load-pct { margin: .7em 0 0; opacity: .9; font-size: .9rem; color: #fff8ea; text-shadow: 0 1px 3px rgba(80,110,90,.4); }
.title-card {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(6px, 1.5vh, 16px);
  text-align: center; color: #fff8ea; max-width: 460px; width: 100%;
  animation: pop .5s cubic-bezier(.2,1.3,.4,1);
  margin: auto;
}
.title-logo {
  font-size: clamp(2rem, min(9vw, 7vh), 5rem);
  filter: drop-shadow(0 8px 0 rgba(120,85,45,.2)); animation: bob 3s ease-in-out infinite;
  position: relative;
}
.title-logo::after {
  content: ""; position: absolute; left: 50%; top: 52%; transform: translate(-50%,-50%);
  width: 130%; height: 130%; border-radius: 50%; z-index: -1;
  background: radial-gradient(circle, rgba(255,206,92,.35), rgba(255,206,92,0) 65%);
  pointer-events: none;
}
.title-card h1 {
  font-size: clamp(1.7rem, min(8vw, 6vh), 4rem); margin: 0; letter-spacing: 1px; color: #fff8ea;
  text-shadow: 0 4px 0 #c47a22, 0 6px 12px rgba(120,85,45,.35);
}
.subtitle {
  font-size: clamp(.8rem, 2vw, 1.1rem); opacity: .95; margin: 0; line-height: 1.45;
  color: #fff8ea; text-shadow: 0 2px 4px rgba(80,110,90,.4);
  white-space: nowrap; width: max-content; max-width: 94vw;
}
@media (max-width: 560px) { .subtitle { white-space: normal; max-width: 30em; width: auto; } }
.title-primary, .title-secondary { display: flex; flex-direction: column; gap: 10px; max-width: 300px; width: 100%; margin: 0 auto; }
.title-secondary { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 8px; max-width: 340px; }
.btn-sm { padding: 8px 16px; font-size: .85rem; }
.title-hint { opacity: .9; font-size: .8rem; color: #fff8ea; text-shadow: 0 1px 3px rgba(80,110,90,.4); }
/* .title-head / .title-actions are pure grouping wrappers around the brand
   text vs. the buttons (kept for markup/JS reasons only — UI._titleSaveBadge
   still looks up #title-continue's parent etc.). They are real flex columns
   carrying the exact same gap .title-card used to apply directly to all its
   children, so nesting two columns reproduces the old flat single-column
   stack pixel-for-pixel in every viewport — portrait, desktop, AND short
   landscape. A row/two-column split was tried here for short landscape
   phones and reverted the same day: the user wants ONE column, unchanged,
   everywhere, even if that means the card has to scroll on a short screen
   (see #title-screen's overflow-y: auto above). Do not reintroduce a
   landscape-specific row layout for this screen.
   `display: contents` was tried before that and reverted too: it renders
   fine in headless Chrome, but iOS Safari has a long-standing bug where
   buttons inside a `display: contents` ancestor stop receiving taps — a
   "looks right in every screenshot, dead on the actual phone" bug. */
.title-head, .title-actions {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(6px, 1.5vh, 16px); width: 100%;
}

/* ---- title-screen wandering crab (vector, walks along the bottom edge) ---- */
.menu-crab {
  position: absolute; bottom: 4%; left: 0; width: 64px; height: auto;
  filter: drop-shadow(0 3px 3px rgba(20,40,35,.3));
  animation: crab-scuttle 18s linear infinite;
  pointer-events: none;
}
.crab-svg { width: 100%; height: auto; display: block; }
.crab-legs { transform-box: fill-box; transform-origin: 50% 50%; animation: crab-leg-swing .35s ease-in-out infinite; }
.crab-legs-b { animation-delay: .175s; }
.crab-claw { transform-box: fill-box; transform-origin: 50% 100%; animation: crab-claw-snip 1.6s ease-in-out infinite; }
.crab-claw-r { animation-delay: .8s; }
@keyframes crab-scuttle {
  from { transform: translateX(-15vw); }
  to   { transform: translateX(115vw); }
}
@keyframes crab-leg-swing {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(6deg); }
}
@keyframes crab-claw-snip {
  0%, 80%, 100% { transform: rotate(0deg); }
  90%           { transform: rotate(-12deg); }
}
@media (max-width: 600px) { .menu-crab { width: 46px; bottom: 3%; } }

/* ---- Veyra the Hooded (real LPC sprite, composited from assets/char/mage_*)
   chasing the crab on the same track, offset start = trails behind ---- */
.menu-hunter {
  position: absolute; bottom: 4%; left: 0; width: 100px; height: 100px;
  filter: drop-shadow(0 4px 3px rgba(20,40,35,.35));
  animation: crab-scuttle 18s linear infinite;
  animation-delay: 1.8s;
  animation-fill-mode: backwards;
  pointer-events: none;
}
.hunter-sprite {
  width: 100%; height: 100%;
  background-image: url("../assets/background/menuHunterWalk.png");
  background-repeat: no-repeat;
  background-size: 900px 100px;
  image-rendering: pixelated;
  animation: hunter-walk-cycle .9s steps(9) infinite;
}
@keyframes hunter-walk-cycle {
  from { background-position: 0 0; }
  to   { background-position: -900px 0; }
}
@media (max-width: 600px) { .menu-hunter { display: none; } }

/* ---- ผีกองกอย (Phi Kong Koi) — hunched one-legged hopping spirit, joins the chase ---- */
.menu-kongkoi {
  position: absolute; bottom: 4%; left: 0; width: 52px; height: auto;
  filter: drop-shadow(0 4px 4px rgba(20,10,10,.35));
  animation: crab-scuttle 18s linear infinite;
  animation-delay: 8.8s;
  animation-fill-mode: backwards;
  pointer-events: none;
}
.kongkoi-svg { width: 100%; height: auto; display: block; }
.kongkoi-hop { transform-box: fill-box; transform-origin: 50% 100%; animation: kongkoi-hop .5s ease-in-out infinite; }
.kongkoi-arms { transform-box: fill-box; transform-origin: 50% 20%; animation: kongkoi-reach .5s ease-in-out infinite; }
@keyframes kongkoi-hop {
  0%, 100% { transform: translateY(0); }
  40%      { transform: translateY(-9px); }
  60%      { transform: translateY(-9px); }
}
@keyframes kongkoi-reach {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(-6deg); }
}
@media (max-width: 600px) { .menu-kongkoi { width: 38px; bottom: 3%; } }

/* ---- กระหัง (Krahang) — flies on winnowing-basket wings, joins the chase overhead ---- */
.menu-krahang {
  position: absolute; bottom: 11%; left: 0; width: 78px; height: auto;
  filter: drop-shadow(0 4px 4px rgba(20,10,10,.3));
  animation: crab-scuttle 18s linear infinite;
  animation-delay: 6s;
  animation-fill-mode: backwards;
  pointer-events: none;
}
.krahang-svg { width: 100%; height: auto; display: block; animation: krahang-bob 1.4s ease-in-out infinite; }
.krahang-wing { transform-box: fill-box; animation: krahang-flap .4s ease-in-out infinite; }
.krahang-wing-l { transform-origin: 90% 50%; }
.krahang-wing-r { transform-origin: 10% 50%; animation-delay: .2s; }
@keyframes krahang-flap {
  0%, 100% { transform: rotate(0deg); }
  50%      { transform: rotate(-10deg); }
}
@keyframes krahang-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}
@media (max-width: 600px) { .menu-krahang { width: 56px; bottom: 9%; } }
#social-dock { position: fixed; right: 16px; bottom: 16px; z-index: 55; display: flex; align-items: center; gap: 10px; }
#fb-follow { display: inline-flex; align-items: center; gap: 8px; padding: 9px 14px 9px 11px; border-radius: 999px; background: #1877f2; color: #fff; font-size: .9rem; font-weight: 700; text-decoration: none; box-shadow: 0 4px 14px rgba(24,119,242,.45); transition: transform .15s ease, box-shadow .15s ease; }
#fb-follow:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(24,119,242,.6); }
#fb-follow svg { flex: none; }
/* hide the whole dock on mobile / touch (matches the old follow-pill behaviour;
   also keeps it off the on-screen attack button during play) */
body.touch-mode #social-dock { display: none !important; }
@media (max-width: 760px), (pointer: coarse) { #social-dock { display: none !important; } }

/* small crab donate button — sits left of the follow pill */
#donate-btn { display: inline-flex; align-items: center; gap: 6px; padding: 9px 13px 9px 11px; border-radius: 999px; border: none; cursor: pointer; background: #e07a5f; color: #fff; font: inherit; font-size: .88rem; font-weight: 700; box-shadow: 0 4px 14px rgba(224,122,95,.5); transition: transform .15s ease, box-shadow .15s ease, filter .15s ease; }
#donate-btn:hover { transform: translateY(-2px); filter: brightness(1.05); box-shadow: 0 6px 18px rgba(224,122,95,.65); }
#donate-btn svg { flex: none; }

/* ---------------- Donate QR modal (redesign) ---------------- */
#donate-screen { z-index: 60; align-items: flex-start; background: rgba(28,40,38,.55); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.donate-card {
  position: relative; box-sizing: border-box;
  width: min(94vw, 560px); max-height: 94vh; overflow-y: auto;
  padding: 22px 30px 24px; text-align: center;
  background: linear-gradient(180deg, #fdf3df, #f7e6c3);
  border: 4px solid var(--wood); border-radius: 30px;
  box-shadow: 0 22px 60px rgba(40,25,10,.45), inset 0 0 0 2px rgba(255,255,255,.5);
  animation: donatePop .28s cubic-bezier(.2,.9,.3,1.2) both;
}
@keyframes donatePop { from { transform: translateY(14px) scale(.96); opacity: 0; } }
.donate-x {
  position: absolute; top: 16px; right: 18px; width: 42px; height: 42px;
  border: 2px solid #e6cfa4; border-radius: 50%; cursor: pointer;
  background: #f3e4c4; color: var(--ink-soft); font-size: 1.5rem; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 6px rgba(0,0,0,.12); transition: transform .15s ease, background .15s ease;
}
.donate-x:hover { background: #ecd6ac; transform: rotate(90deg); }

.donate-title { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 2px 0 8px; font-size: 1.5rem; font-weight: 800; color: var(--ink); }
.donate-title .spark { flex: none; }
.donate-title .spark-l { align-self: flex-start; margin-top: 2px; }
.donate-title .spark-r { align-self: flex-end; margin-bottom: 6px; }
.donate-lead { margin: 0 auto 12px; max-width: 420px; font-size: .95rem; line-height: 1.5; color: var(--ink); opacity: .92; }

.donate-qr-card {
  position: relative; margin: 0 auto; max-width: 440px;
  padding: 24px 20px 16px; background: #fff; border-radius: 22px;
  box-shadow: 0 8px 22px rgba(90,60,25,.16);
}
.donate-ribbon {
  position: absolute; top: -16px; left: 50%; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
  padding: 8px 26px; color: #fff; font-weight: 800; font-size: 1rem;
  background: linear-gradient(180deg, #9cbf5f, #7fa548);
  border-radius: 8px; box-shadow: 0 4px 10px rgba(90,110,40,.35);
}
.donate-ribbon::before, .donate-ribbon::after {
  content: ""; position: absolute; top: 100%; border: 8px solid transparent;
}
.donate-ribbon::before { left: 4px; border-top-color: #5f7d33; border-right-color: #5f7d33; }
.donate-ribbon::after { right: 4px; border-top-color: #5f7d33; border-left-color: #5f7d33; }
.donate-ribbon .leaf { flex: none; }
.donate-ribbon .leaf-r { transform: scaleX(-1); }

.donate-qr-wrap { display: flex; align-items: center; justify-content: center; gap: 6px; margin: 4px 0 2px; }
.donate-qr-wrap img { display: block; width: 190px; max-width: 50vw; height: auto; border-radius: 6px; }
.donate-qr-wrap .burst { flex: none; opacity: .85; }
.donate-acct { margin: 8px auto 0; display: flex; flex-direction: column; gap: 2px; padding-top: 8px; border-top: 1px dashed #e2d0ac; }
.donate-acct .acct-name { font-size: 1rem; font-weight: 800; color: var(--ink); }
.donate-acct .acct-no { font-size: .9rem; color: var(--ink-soft); letter-spacing: .5px; }
.donate-note { margin: 6px 0 0; font-size: .85rem; color: var(--ink-soft); }

/* "บอกชื่อที่อยากให้โชว์" — the opt-in ask that feeds the wall below it.
   Always present (a PromptPay transfer is anonymous to us), unlike the wall. */
.donate-ask { margin: 14px auto 0; max-width: 440px; padding: 12px 16px 14px; background: rgba(232,240,216,.7); border-radius: 18px; }
.donate-ask .da-line { margin: 0; font-size: .84rem; line-height: 1.5; color: #4c5f36; }
.donate-ask .da-btn { margin-top: 10px; padding: 9px 18px; border: none; border-radius: 999px; cursor: pointer; font: inherit; font-size: .88rem; font-weight: 700; color: #fff; background: linear-gradient(#9cbf5f,#7fa548); box-shadow: 0 3px 0 #5f7d33; transition: transform .12s ease, filter .12s ease; }
.donate-ask .da-btn:hover { transform: translateY(-1px); filter: brightness(1.05); }
.donate-ask .da-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #5f7d33; }

/* supporters' wall — hand-written names from js/data/donors.js. The whole block
   is absent (UI._renderDonors) while that list is empty, so it costs the card
   nothing until the first name lands. */
.donate-thanks { margin: 16px auto 0; max-width: 440px; padding: 12px 16px 14px; background: rgba(255,255,255,.6); border: 2px dashed #e2d0ac; border-radius: 18px; }
.donate-thanks .dt-head { display: flex; align-items: center; justify-content: center; gap: 6px; font-weight: 800; font-size: 1rem; color: var(--ink); }
.donate-thanks .dt-head svg { flex: none; }
.donate-thanks .dt-list { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 7px; margin-top: 10px; }
.donate-thanks .dt-name { display: inline-flex; align-items: baseline; gap: 5px; padding: 5px 12px; background: #fff; border-radius: 999px; box-shadow: 0 2px 6px rgba(90,60,25,.14); font-size: .92rem; font-weight: 700; color: var(--ink); }
.donate-thanks .dt-name i { font-style: normal; font-weight: 500; font-size: .8rem; color: var(--ink-soft); }
.donate-thanks .dt-foot { margin-top: 10px; font-size: .8rem; color: var(--ink-soft); }

.donate-mascot-row { display: flex; align-items: flex-end; justify-content: center; gap: 10px; margin-top: 10px; }
.donate-crab { flex: none; width: 84px; height: auto; }
.donate-crab .crab-heart { animation: crabHeart 2.4s ease-in-out infinite; transform-origin: center; }
.donate-crab .crab-heart:nth-of-type(2) { animation-delay: .9s; }
@keyframes crabHeart { 0%,100% { opacity: .5; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-4px); } }
.donate-bubble {
  position: relative; max-width: 280px; text-align: left;
  padding: 8px 14px; margin-bottom: 6px;
  background: #e8f0d8; color: #4c5f36; font-size: .85rem; line-height: 1.4;
  border-radius: 16px; border-bottom-left-radius: 4px;
  box-shadow: 0 3px 8px rgba(90,110,40,.18);
}
.donate-bubble::before {
  content: ""; position: absolute; left: -9px; bottom: 8px;
  border: 6px solid transparent; border-right-color: #e8f0d8; border-bottom-color: #e8f0d8;
}

@media (max-width: 480px) {
  .donate-card { padding: 24px 18px 26px; border-radius: 24px; }
  .donate-title { font-size: 1.35rem; }
  .donate-lead { font-size: .92rem; }
  .donate-mascot-row { flex-direction: column; align-items: center; gap: 6px; }
  .donate-bubble { text-align: center; border-bottom-left-radius: 16px; }
  .donate-bubble::before { display: none; }
}

/* ---------------- Panels (modal slide-in) ---------------- */
.panel {
  background: linear-gradient(180deg, #fffaf0, #fbeccb);
  border-radius: 26px; border: 3px solid var(--wood);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 18px 44px rgba(120,85,45,.35);
  max-width: 560px; width: 100%; max-height: 86vh; display: flex; flex-direction: column;
  animation: pop .25s cubic-bezier(.2,1.2,.4,1); color: var(--ink);
}
.how-panel { position: relative; max-width: 480px; padding: 0; display: flex; flex-direction: column; }
.how-body { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 24px 28px 8px; }
.how-panel h2 { margin-top: 0; color: var(--gold-hi); }
.how-list { line-height: 1.7; padding-left: 1.1em; }
.how-list li { margin-bottom: 4px; }
.how-panel .btn { display: block; margin: 12px auto 16px; flex-shrink: 0; }

#panel-overlay {
  position: fixed; inset: 0; z-index: 55; display: flex; align-items: center;
  justify-content: center; padding: 16px; background: rgba(94,67,31,.28); backdrop-filter: blur(3px);
}
.panel-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px; border-bottom: 2.5px dashed var(--wood);
}
.panel-header h2 { margin: 0; color: var(--gold-hi); font-size: 1.4rem; }
.panel-title-ic { width: 1.05em; height: 1.05em; vertical-align: -0.15em; margin-right: 7px; color: var(--gold-hi); }
/* overscroll-behavior:contain stops iOS from chaining the list's scroll to the
   page once it hits an edge (the "background pans while I scroll the menu" bug). */
#panel-body { padding: 16px 20px; overflow-y: auto; flex: 1 1 auto; min-height: 0; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }

/* ---------------- HUD root ---------------- */
#hud { position: fixed; inset: 0; z-index: 20; pointer-events: none; }
#hud > * { pointer-events: auto; }
.ic { width: 1.5rem; height: 1.5rem; display: block; color: var(--wood-dark); }

/* ---------------- Top-left: portrait + bars ---------------- */
#player-hud {
  position: absolute; top: 14px; left: 14px;
  display: flex; align-items: flex-start; gap: 12px;
}
.portrait-frame { position: relative; width: 92px; height: 92px; flex-shrink: 0; }
.portrait-frame .exp-ring {
  position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(var(--xp) calc(var(--p,0) * 1%), rgba(138,95,51,.22) 0);
  padding: 4px;
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #000 calc(100% - 4px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 5px), #000 calc(100% - 4px));
}
#hud-portrait {
  position: absolute; top: 7px; left: 7px; width: 78px; height: 78px;
  border-radius: 50%; background: radial-gradient(circle at 50% 35%, #fff8ea, #f6e1b9);
  border: 3px solid #fff8ea;
  box-shadow: 0 0 0 2px var(--wood), 0 3px 8px rgba(120,85,45,.3);
}
.portrait-frame .exp-tag {
  position: absolute; bottom: -4px; left: 50%; transform: translateX(-50%);
  font-size: .56rem; font-weight: 800; letter-spacing: .5px; color: #6e4310;
  background: linear-gradient(#ffe19a, var(--gold)); padding: 1px 8px; border-radius: 999px;
  border: 1.5px solid #fff8ea; box-shadow: 0 2px 4px rgba(120,85,45,.3);
}
.portrait-frame .lvl-badge {
  position: absolute; top: -3px; right: -5px; min-width: 26px; height: 26px; padding: 0 5px;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 40% 30%, #fff5da, #f5a73f);
  border: 2.5px solid #fff8ea; border-radius: 50%;
  font-weight: 800; font-size: .8rem; color: #6e4310; box-shadow: 0 2px 5px rgba(120,85,45,.35);
}

.player-meta { display: flex; flex-direction: column; gap: 6px; padding-top: 4px; min-width: 180px; }

#stat-panel { display: flex; flex-direction: column; gap: 6px; }
.stat { display: flex; align-items: center; gap: 7px; }
.stat-icon { width: 1.1rem; height: 1.1rem; flex-shrink: 0; filter: drop-shadow(0 1px 1px rgba(255,255,255,.7)); }
.stat[data-stat="health"] .stat-icon { color: var(--hp); }
.stat[data-stat="hunger"] .stat-icon { color: var(--food); }
.stat[data-stat="thirst"] .stat-icon { color: var(--water); }
.stat[data-stat="stamina"] .stat-icon { color: var(--stamina); }
.bar {
  position: relative; width: 176px; height: 16px; border-radius: 999px;
  background: rgba(255,248,234,.85); overflow: hidden;
  border: 2px solid #fff8ea; box-shadow: 0 0 0 1.5px var(--wood), inset 0 1px 2px rgba(120,85,45,.18);
}
.bar .fill { height: 100%; width: 100%; border-radius: 999px; transition: width .25s ease, background .3s ease; }
.bar .stat-val {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: .68rem; font-weight: 800; color: #fff; letter-spacing: .3px;
  text-shadow: 0 1px 1.5px rgba(94,67,31,.85); font-variant-numeric: tabular-nums; pointer-events: none;
}
.stat[data-stat="health"] .fill { background: linear-gradient(180deg, var(--hp2), var(--hp)); }
.stat[data-stat="hunger"] .fill { background: linear-gradient(180deg, var(--food2), var(--food)); }
.stat[data-stat="thirst"] .fill { background: linear-gradient(180deg, var(--water2), var(--water)); }
.stat[data-stat="stamina"] .fill { background: linear-gradient(180deg, var(--stamina2), var(--stamina)); }
.bar .fill.danger { animation: bar-pulse 0.9s ease-in-out infinite; }
.stat-icon.danger { animation: icon-shake 1.2s ease-in-out infinite; }
/* stamina hit empty → locked out from swinging for a beat; the bar greys
   out and pulses so it reads as "recovering", distinct from the red danger
   pulse used for hunger/thirst/health running low. */
.bar .fill.cooldown { background: linear-gradient(180deg, #cfcabf, #a6a196) !important; animation: bar-pulse 0.6s ease-in-out infinite; }
.stat-icon.cooldown { opacity: .5; }

/* ---------------- Left vertical nav ---------------- */
#side-nav {
  position: absolute; top: 50%; left: 14px; transform: translateY(-50%);
  display: flex; flex-direction: column; gap: 10px;
}
#side-nav.collapsed .nav-btn { display: none; }
#side-nav .nav-btn {
  position: relative; width: 62px; height: 62px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 2.5px solid var(--wood); border-radius: 18px; cursor: pointer;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 4px 0 rgba(138,95,51,.28), 0 6px 12px rgba(120,85,45,.18);
  transition: transform .1s ease, box-shadow .12s, filter .15s;
}
#side-nav .nav-btn .ic { width: 1.7rem; height: 1.7rem; color: var(--wood-dark); }
#side-nav .nav-btn label { font-size: .58rem; font-weight: 800; color: var(--ink-soft); letter-spacing: .3px; text-transform: uppercase; cursor: pointer; }
#side-nav .nav-btn:hover { filter: brightness(1.03); transform: translateY(-1px); }
#side-nav .nav-btn:active { transform: translateY(3px); box-shadow: inset 0 0 0 2px #fff8ea, 0 1px 0 rgba(138,95,51,.28); }
#nav-collapse {
  align-self: center; width: 28px; height: 28px; margin-top: 2px;
  background: linear-gradient(#fffaf0, #f6e1b9); border: 2px solid var(--wood); border-radius: 50%;
  color: var(--wood-dark); font-weight: 800; cursor: pointer; line-height: 1;
  box-shadow: 0 2px 0 rgba(138,95,51,.28); transition: transform .25s;
}
#side-nav.collapsed #nav-collapse { transform: rotate(180deg); }

/* ---------------- Top-right: region + minimap ---------------- */
#top-right {
  position: absolute; top: 14px; right: 14px;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}
#region-bar {
  display: flex; align-items: center; gap: 8px;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 2.5px solid var(--wood); border-radius: 999px; padding: 5px 9px 5px 15px;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 4px 10px rgba(120,85,45,.18);
}
.rb-text { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.05; }
#region-name { font-size: .95rem; font-weight: 800; color: var(--ink); }
#village-level { font-size: .66rem; font-weight: 800; color: var(--gold-hi); letter-spacing: .5px; }
.rb-sun { width: 1.6rem; height: 1.6rem; color: #f5a73f; filter: drop-shadow(0 0 5px rgba(255,206,92,.7)); }
.rb-sun.rb-night { color: #b9cdf5; filter: drop-shadow(0 0 5px rgba(170,195,255,.6)); }
.rb-sun.rb-rain { color: #8fb6d9; filter: drop-shadow(0 0 5px rgba(140,180,220,.6)); }

/* ---- field journal (codex) panel ---- */
.cdx-progress { font-weight: 800; font-size: 1.05rem; margin-bottom: .15rem; }
/* one reward rung — used by BOTH the hunter's build reward (creature tab) and
   every relic-set rung, so "collect N, keep it forever" always looks the same */
.cdx-rw { margin: 0 0 .45rem; padding: .5rem .65rem; border-radius: .65rem;
  background: linear-gradient(#fffaf0, #f7ead0); border: 2px solid var(--wood);
  box-shadow: 0 2px 0 rgba(138,95,51,.16); }
.cdx-rw-top { display: flex; align-items: center; justify-content: space-between; gap: .5rem; font-size: .86rem; }
.cdx-rw-top b { font-weight: 800; }
.cdx-rw-n { font-variant-numeric: tabular-nums; font-weight: 800; font-size: .8rem; color: var(--ink-soft); }
.cdx-rw-bar { height: 7px; margin: .34rem 0 .3rem; border-radius: 999px;
  background: rgba(138,95,51,.18); overflow: hidden; }
.cdx-rw-bar i { display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #ffd982, #f0a52a); transition: width .35s ease; }
.cdx-rw-sub { font-size: .76rem; line-height: 1.3; color: var(--ink-soft); }
.cdx-rw.on { border-color: #5fbf63; background: linear-gradient(#f4fbf1, #dff3dc); }
.cdx-rw.on .cdx-rw-bar i { background: linear-gradient(90deg, #a9e79c, #4fae57); }
.cdx-rw.on .cdx-rw-sub { color: #2f6b36; }
/* tab strip: label over its own found/total counter */
.cdx-tabs { margin-bottom: .6rem; }
.cdx-tabs .char-tab { padding: 7px 6px; font-size: .8rem; line-height: 1.2; }
.cdx-tab-n { display: block; font-size: .68rem; font-weight: 800; opacity: .7; font-variant-numeric: tabular-nums; }
.cdx-hint { opacity: .65; font-size: .85rem; margin-bottom: .6rem; }
.cdx-h { margin: .7rem 0 .3rem; font-size: .95rem; opacity: .85; border-bottom: 1px solid rgba(255,255,255,.12); padding-bottom: .2rem; }
.cdx-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: .45rem; }
.cdx-card { position: relative; display: flex; flex-direction: column; align-items: center; gap: .15rem;
  padding: .5rem .3rem .4rem; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.08);
  border-radius: .6rem; cursor: pointer; transition: background .12s, transform .12s; }
.cdx-card:hover { background: rgba(255,255,255,.1); transform: translateY(-1px); }
.cdx-card b { font-weight: 700; font-size: .78rem; text-align: center; line-height: 1.15; }
.cdx-card.cdx-unknown { opacity: .6; }
.cdx-card.cdx-unknown b { opacity: .5; letter-spacing: .12em; }
.cdx-cv { width: 72px; height: 72px; image-rendering: pixelated; }
.cdx-cv-big { width: 128px; height: 128px; }
.cdx-fico { font-size: 2.6rem; line-height: 72px; height: 72px; }
.cdx-fico-big { font-size: 4.5rem; line-height: 128px; height: 128px; }
.cdx-n { font-variant-numeric: tabular-nums; opacity: .8; font-size: .78rem; }
.cdx-boss { font-size: .6rem; font-weight: 900; letter-spacing: .05em; color: #ffb46b; border: 1px solid #ffb46b; border-radius: .3rem; padding: 0 .3rem; }
/* fish journal: rarity-tinted cards, tier badge, weight/hint line */
.cdx-fishcard { border: 1.5px solid var(--rare, rgba(255,255,255,.14)); }
.cdx-fishcard.cdx-unknown { border-color: rgba(255,255,255,.12); }
.cdx-rarity { font-size: .56rem; font-weight: 800; letter-spacing: .02em; border: 1px solid currentColor; border-radius: .3rem; padding: 0 .28rem; opacity: .92; }
.cdx-hint-sm { font-size: .58rem; opacity: .62; text-align: center; line-height: 1.12; letter-spacing: 0; }
/* relic shelf: crimson-tinted cards (matching GEAR_TIERS[6].color) + the set ladder */
.cdx-relcard { border: 1.5px solid rgba(255,77,109,.55); background: rgba(255,77,109,.07); }
.cdx-relcard.cdx-unknown { border-color: rgba(255,255,255,.12); background: rgba(255,255,255,.05); }
.cdx-relcard .cdx-fico { display: flex; align-items: center; justify-content: center; }
.cdx-relcard .cdx-fico img { width: 46px; height: 46px; image-rendering: pixelated; }
.cdx-relrarity { color: #ff4d6d; }
.cdx-h-n { font-variant-numeric: tabular-nums; opacity: .6; font-weight: 700; }
.cdx-set { margin-bottom: .5rem; }
.cdx-uses { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: center; }
.cdx-use { display: flex; align-items: center; gap: .3rem; font-size: .8rem;
  padding: .28rem .5rem; border-radius: .45rem; background: rgba(255,255,255,.05); }
.cdx-usen { opacity: .7; font-variant-numeric: tabular-nums; }
.cdx-backbtn { margin-bottom: .5rem; }
.cdx-detail { display: flex; flex-direction: column; align-items: center; text-align: center; }
.cdx-stage { width: 100%; display: flex; justify-content: center; padding: .6rem 0;
  background: radial-gradient(ellipse at center, rgba(255,255,255,.08), transparent 70%); border-radius: .8rem; }
.cdx-name { margin: .35rem 0 .5rem; font-size: 1.2rem; }
.cdx-stats { display: grid; grid-template-columns: 1fr 1fr; gap: .3rem .6rem; width: 100%; max-width: 24rem; margin-bottom: .4rem; }
.cdx-stat { display: flex; justify-content: space-between; gap: .6rem; padding: .3rem .55rem;
  background: rgba(255,255,255,.05); border-radius: .5rem; font-size: .82rem; }
.cdx-stat span { opacity: .65; }
.cdx-lore { max-width: 26rem; text-align: left; line-height: 1.55; font-size: .88rem; opacity: .92;
  background: rgba(255,255,255,.04); border-left: 3px solid rgba(255,200,120,.5); padding: .55rem .7rem; border-radius: .4rem; }
.cdx-lore.cdx-locked { opacity: .55; font-style: italic; border-left-color: rgba(255,255,255,.2); }
#minimap {
  position: relative; width: 118px; height: 118px; border-radius: 50%;
  background: radial-gradient(circle at 50% 38%, #aee0bd, #6fbf94);
  border: 4px solid var(--wood);
  box-shadow: inset 0 0 0 3px #fff8ea, inset 0 0 18px rgba(90,140,100,.35), 0 5px 14px rgba(120,85,45,.22);
}
.mm-day {
  position: absolute; top: -8px; left: 50%; transform: translateX(-50%);
  font-size: .6rem; font-weight: 800; color: #6e4310; letter-spacing: .3px;
  background: linear-gradient(#ffe19a, var(--gold)); padding: 1px 9px; border-radius: 999px;
  border: 1.5px solid #fff8ea; box-shadow: 0 2px 4px rgba(120,85,45,.3); white-space: nowrap;
}
#mm-canvas {
  position: absolute; inset: 6px; width: calc(100% - 12px); height: calc(100% - 12px);
  border-radius: 50%; image-rendering: auto;
}
.mm-dot {
  position: absolute; top: 50%; left: 50%; width: 10px; height: 10px; margin: -5px;
  background: #ff8f6e; border: 2.5px solid #fff8ea; border-radius: 50%;
  box-shadow: 0 0 8px rgba(255,143,110,.8);
}
.mm-n { position: absolute; top: 7px; left: 50%; transform: translateX(-50%); font-size: .62rem; font-weight: 800; color: rgba(255,255,255,.85); text-shadow: 0 1px 2px rgba(90,140,100,.5); }

/* full Island Map panel */
.map-wrap { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 6px 4px 12px; }
#map-canvas {
  width: min(72vh, 440px); max-width: 100%; aspect-ratio: 1; height: auto;
  border-radius: 14px; border: 3px solid var(--wood); background: #274d63;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 16px rgba(120,85,45,.25);
}
.map-legend { display: flex; flex-wrap: wrap; gap: 6px 14px; justify-content: center; max-width: 440px; font-size: .78rem; font-weight: 700; color: #6e4310; }
.map-legend span { display: inline-flex; align-items: center; gap: 5px; }
.map-legend i { width: 11px; height: 11px; border-radius: 3px; display: inline-block; box-shadow: 0 0 0 1px rgba(0,0,0,.12); }
.map-legend i.dot { border-radius: 50%; }

/* ---------------- Right: chapter / quest tracker ---------------- */
#quest-tracker {
  position: absolute; top: 192px; right: 14px; width: 250px; max-width: calc(100vw - 28px);
  text-align: left; cursor: pointer;
  background: linear-gradient(180deg, #fffaf0, #fbeccb);
  border: 2.5px solid var(--wood); border-radius: 20px; padding: 0 0 10px;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 16px rgba(120,85,45,.22);
  transition: top .35s ease, right .35s ease, transform .35s cubic-bezier(.4,0,.2,1), border-radius .35s ease, filter .15s ease;
  overflow: hidden;
}
#quest-tracker:hover { filter: brightness(1.03); }
#quest-tracker:not(.qt-minimized):active { transform: translateY(1px); }
.qt-close {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--wood-mid); font-size: 1rem; font-weight: 900; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
  transition: background .15s, color .15s;
}
.qt-close:hover { background: rgba(210,168,110,.35); color: var(--bad); }
#quest-tracker.qt-minimized {
  top: calc(50% - 22px); right: 0;
  transform: translateX(calc(100% - 36px));
  padding-bottom: 0; border-radius: 20px 0 0 20px;
}
#quest-tracker.qt-minimized .qt-name,
#quest-tracker.qt-minimized .qt-goals,
#quest-tracker.qt-minimized .qt-rewards-preview,
#quest-tracker.qt-minimized .qt-claim-btn,
#quest-tracker.qt-minimized .qt-toggle,
#quest-tracker.qt-minimized .qt-close,
#quest-tracker.qt-minimized .qt-label { display: none; }
#quest-tracker.qt-minimized .qt-head { border-bottom: none; padding: 11px 9px; }
#quest-tracker.qt-minimized:hover { transform: translateX(calc(100% - 46px)); filter: brightness(1.05); }
.qt-head {
  display: flex; align-items: center; gap: 8px; padding: 10px 14px;
  background: linear-gradient(180deg, rgba(255,206,92,.3), rgba(255,206,92,0));
  border-bottom: 2px dashed var(--wood);
}
.qt-icon { color: var(--gold-hi); display: flex; }
.qt-icon svg { width: 1.05rem; height: 1.05rem; }
.qt-label { font-size: .78rem; font-weight: 800; color: var(--gold-hi); letter-spacing: .3px; flex: 1; }
.qt-toggle {
  background: none; border: none; padding: 2px; cursor: pointer;
  color: var(--wood-mid); display: flex; align-items: center; margin-left: auto;
  border-radius: 50%; width: 24px; height: 24px; justify-content: center;
  transition: background .15s, transform .2s;
}
.qt-toggle:hover { background: rgba(210,168,110,.3); }
.qt-toggle svg { width: 14px; height: 14px; transition: transform .2s ease; }
#quest-tracker.collapsed .qt-toggle svg { transform: rotate(-90deg); }
#quest-tracker.collapsed .qt-name,
#quest-tracker.collapsed .qt-goals,
#quest-tracker.collapsed .qt-rewards-preview,
#quest-tracker.collapsed .qt-claim-btn { display: none; }
#quest-tracker.collapsed { padding-bottom: 0; }
#quest-tracker.collapsed .qt-head { border-bottom: none; }
.qt-name { font-weight: 800; color: var(--ink); font-size: .95rem; margin: 9px 14px 5px; line-height: 1.25; }
.qt-goals { display: flex; flex-direction: column; gap: 8px; padding: 6px 14px 4px; }
/* goal: two-line layout — label+count on top, bar+chips below */
.qt-goal-wrap { display: flex; flex-direction: column; gap: 3px; }
.qt-goal-line {
  display: flex; align-items: center; gap: 6px;
  font-size: .81rem; color: var(--ink-soft); line-height: 1.3;
}
.qt-goal-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qt-goal-wrap.met .qt-goal-line { color: var(--leaf-dark); }
.qt-val { font-weight: 800; font-variant-numeric: tabular-nums; font-size: .78rem; color: var(--ink-soft); flex-shrink: 0; }
.qt-goal-wrap.met .qt-val { color: var(--leaf-dark); }
.qt-goto-arrow { font-size: .65rem; color: var(--gold-hi); font-weight: 800; flex-shrink: 0; }
.qt-tick { flex-shrink: 0; width: 15px; height: 15px; border-radius: 50%; border: 2px solid var(--wood); background: #fff8ea; display: inline-block; position: relative; }
.qt-goal-wrap.met .qt-tick { border-color: var(--leaf); background: var(--leaf); }
.qt-goal-wrap.met .qt-tick::after { content: ""; position: absolute; left: 3.5px; top: 1px; width: 4px; height: 7px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(40deg); }
.qt-bar-row { display: flex; align-items: center; gap: 6px; padding-left: 21px; }
.qt-chips { display: flex; gap: 6px; flex-wrap: wrap; }
/* one-line "where to find it" under an unmet gather goal — the tracker is a
   HUD widget, so it clips instead of wrapping and stays visually subordinate
   to the goal text above it. */
.qt-src { padding-left: 21px; font-size: .66rem; font-weight: 700; color: var(--ink-soft); line-height: 1.35; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qt-chip { font-size: .72rem; font-weight: 800; }
.qt-chip.ok { color: var(--leaf-dark); }
.qt-chip.miss { color: var(--bad); }

/* tappable goal rows — players didn't realise the line jumps to the craft/build
   panel. The row stays quiet while you're still gathering; the MOMENT the goal is
   doable a green "do it now!" pill pops up (the only cue), matching the palette of
   the primary Craft button so nothing reads as off-theme. */
.qt-goal-wrap.qt-tappable {
  cursor: pointer; padding: 5px 7px; margin: 0 -3px; border-radius: 12px;
  border: 1.5px solid transparent; transition: background .12s, border-color .12s, transform .08s;
}
.qt-goal-wrap.qt-tappable:hover { background: rgba(234,165,62,.1); border-color: var(--gold-lo); }
.qt-goal-wrap.qt-tappable:active { transform: translateY(1px); }
/* ready → warm gold tint on the row (matches the cream pill + the game's gold
   "ready/attention" language, e.g. the claim button) */
.qt-goal-wrap.qt-ready { background: rgba(234,165,62,.14); border-color: var(--gold); }
.qt-cta-row { display: flex; padding-left: 21px; margin-top: 2px; }
.qt-cta {
  display: inline-flex; align-items: center; gap: 3px; font-size: .7rem; font-weight: 800;
  border-radius: 999px; padding: 2px 10px; line-height: 1.3;
  /* cream pill, like the region label — wood border + hard tan shadow keeps it
     reading as a raised button; a gentle scale pulse still draws the eye. */
  color: var(--wood-dark); background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 1.5px solid var(--wood); box-shadow: 0 2px 0 var(--wood-mid);
  animation: qtCtaPulse 1.1s ease-in-out infinite;
}
.qt-cta-arrow { font-size: .62rem; color: var(--gold-hi); }
@keyframes qtCtaPulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.06); } }
.qt-claim {
  margin: 8px 14px 0; font-size: .76rem; font-weight: 800; color: var(--gold-hi);
  animation: bar-pulse 1.4s ease-in-out infinite;
}

/* Both cues live on one centered row so they always align on the same level.
   Centering happens here (not on the children) so the interact pill's bob
   animation — which owns `transform` — no longer knocks it off-centre. */
#prompt-row {
  position: absolute; left: 50%; bottom: 176px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 10px; white-space: nowrap;
}

#interact-prompt {
  background: linear-gradient(180deg, #fffaf0, #f6e1b9); color: var(--wood-dark); padding: 8px 18px; border-radius: 999px;
  font-weight: 800; box-shadow: 0 4px 0 rgba(138,95,51,.28); border: 2px solid var(--wood);
  animation: bob 1.6s ease-in-out infinite; white-space: nowrap;
  cursor: pointer; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent;
}
#interact-prompt:active { transform: translateY(1px); }

/* Move hint — same amber palette as the interact pill so they read as one set;
   kept the secondary cue (moving isn't the main action) by being smaller, no
   bob, and slightly dimmer. Desktop only (touch uses #move-btn). */
#move-hint {
  display: flex; align-items: center; gap: 6px;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9); color: var(--wood-dark); padding: 5px 13px; border-radius: 999px;
  font-size: .85em; font-weight: 700; box-shadow: 0 3px 0 rgba(138,95,51,.28); border: 2px solid var(--wood);
  white-space: nowrap; opacity: .9;
}
#move-hint .mh-icon { width: 1rem; height: 1rem; color: var(--wood-dark); }
body.touch-mode #move-hint { display: none; } /* touch gets the round button instead */

/* Cook hint — same secondary-cue treatment as #move-hint, just its own key (F)
   so it never gets confused with the rest/sleep meaning of E. Desktop only. */
#cook-hint {
  display: flex; align-items: center; gap: 6px;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9); color: var(--wood-dark); padding: 5px 13px; border-radius: 999px;
  font-size: .85em; font-weight: 700; box-shadow: 0 3px 0 rgba(138,95,51,.28); border: 2px solid var(--wood);
  white-space: nowrap; opacity: .9;
}
body.touch-mode #cook-hint { display: none; } /* touch gets the round button instead */

/* ---------------- Discovery / toasts ---------------- */
#toast-area {
  /* Centered, hugging the top edge — sits below the daily-reward button
     (top:56–64px, ~34px tall) so the two never overlap. A top-level sibling
     of #hud/#panel-overlay (not nested inside #hud) — position:fixed anchors
     it the same way #hud's inset:0 used to, but z-index 110 now actually
     stacks above every open panel (max 101) instead of being trapped under
     #hud's z:20 stacking context, which used to swallow toasts (e.g. "hotbar
     full") behind an open panel. */
  position: fixed; left: 50%; top: calc(112px + env(safe-area-inset-top)); transform: translateX(-50%); z-index: 110;
  display: flex; flex-direction: column; gap: 8px; align-items: center; width: max-content; max-width: 90vw;
}
.toast {
  display: flex; align-items: center; gap: 6px;
  background: linear-gradient(180deg, #fffaf0, #fbeccb); color: var(--ink);
  padding: 10px 22px; border-radius: 999px; font-weight: 800;
  border: 2.5px solid var(--wood);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 14px rgba(120,85,45,.25);
  animation: toastIn .3s ease, toastOut .4s ease forwards; animation-delay: 0s, 2.4s;
}
.toast.warn { border-color: var(--warn); }
.toast.bad  { border-color: var(--bad); color: #a33b30; }
.toast.quest { border-color: var(--gold); color: var(--gold-hi); }
/* actionable toast (a CTA the player taps) — cursor + a soft golden pulse to
   invite the tap, and it drops the base toast's auto fade-out (JS removes it
   after ~8s) so the target stays reachable long enough to hit on mobile. */
.toast.tappable {
  cursor: pointer; border-color: var(--gold);
  animation: toastIn .3s ease, toastTapPulse 1.6s ease-in-out .4s infinite;
}
@keyframes toastTapPulse {
  0%, 100% { box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 14px rgba(120,85,45,.25); }
  50%      { box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 18px rgba(224,170,66,.6); }
}

/* Live-update banner — persistent (unlike toasts, which auto-dismiss) and
   above every HUD layer. top:112px + the safe-area inset reuses the same
   clearance #toast-area relies on to duck under the top-left portrait
   (clickable — opens the Hero panel) and the top-right daily-reward button;
   sitting any higher would park this over one of them for the whole session
   since — unlike a toast — nothing here auto-dismisses it. */
#update-banner {
  position: fixed; left: 50%; top: calc(112px + env(safe-area-inset-top));
  transform: translateX(-50%); z-index: 300;
  max-width: 92vw; padding: 10px 22px; border-radius: 999px; cursor: pointer;
  background: linear-gradient(180deg, #fffaf0, #fbeccb); color: var(--ink);
  font-weight: 800; border: 2.5px solid var(--gold);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 14px rgba(120,85,45,.35);
  animation: toastIn .3s ease;
}

/* ---------------- Bottom bar (system log + dock + spacer) ---------------- */
#bottom-bar {
  position: absolute; left: 14px; right: 124px; bottom: 14px;
  display: flex; align-items: stretch; gap: 12px;
  pointer-events: none;
}
#bottom-bar > * { pointer-events: auto; }
#bottom-bar-fill { flex: 1; max-width: 300px; pointer-events: none; }

#system-log {
  flex: 1; max-width: 300px;
  display: flex; flex-direction: column; justify-content: center; gap: 3px;
  background: linear-gradient(180deg, #fffaf0, #f1ddb4);
  border: 3px solid var(--wood); border-radius: 22px;
  padding: 8px 14px;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 14px rgba(120,85,45,.25);
  font-size: .8rem; color: var(--ink-soft);
  transition: opacity .6s ease;
}
/* Touch: dim & auto-hide so the box never blocks the small screen.
   It fades out a few seconds after the last message (JS adds .sl-auto-hidden)
   and fades back in when a new message arrives. */
body.touch-mode #system-log {
  opacity: .82;
  background: linear-gradient(180deg, rgba(255,250,240,.6), rgba(241,221,180,.6));
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
body.touch-mode #system-log.sl-auto-hidden { opacity: 0; pointer-events: none; }
.sl-header { display: flex; align-items: center; gap: 8px; }
.sl-icon { width: 1.1rem; height: 1.1rem; color: var(--gold-hi); flex-shrink: 0; }
.sl-tag { font-weight: 800; color: var(--gold-hi); font-size: .72rem; padding: 1px 8px; border: 1.5px solid var(--gold-lo); border-radius: 999px; background: #fff8ea; flex-shrink: 0; }
.sl-status { margin-left: auto; color: var(--gold-hi); font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; font-size: .72rem; flex-shrink: 0; }
.sl-close { margin-left: auto; background: none; border: none; cursor: pointer; color: var(--gold-hi); font-size: .85rem; line-height: 1; padding: 0 2px; opacity: .7; }
.sl-close:active { opacity: 1; }
#system-log.sl-hidden { display: none !important; }
#system-log.sl-collapsed #system-msg { display: none; }
/* No clamp: the box grows upward instead of chopping the message. Thai has no
   inter-word spaces, so a clipped line lands mid-word and the player can't tell
   what got cut. */
#system-msg {
  font-weight: 700;
  white-space: normal; overflow-wrap: anywhere;
}
/* ---------------- multiplayer chat (WEB ONLY) ----------------
   Its own box at the BOTTOM-RIGHT, floating above the bottom bar. Kept faint
   so it doesn't compete with the world; it brightens on hover / while typing.
   Disabled entirely on mobile (see body.touch-mode rule below). */
#chat-box {
  position: absolute; right: 14px; left: auto; bottom: 96px; z-index: 6;
  width: min(300px, 40vw);
  display: flex; flex-direction: column; gap: 4px;
  background: linear-gradient(180deg, rgba(255,250,240,.4), rgba(241,221,180,.4));
  border: 2px solid rgba(138,95,51,.38); border-radius: 16px;
  padding: 7px 11px;
  box-shadow: 0 4px 10px rgba(120,85,45,.12);
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  font-size: .8rem; color: var(--ink-soft);
  opacity: 1;   /* always at full (hover) brightness — no idle fade */
}
#chat-box.hidden { display: none !important; }
.chat-head { display: flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.chat-min {
  margin-left: auto; background: none; border: none; cursor: pointer;
  color: var(--gold-hi); font-size: .9rem; line-height: 1; padding: 0 2px; opacity: .75;
}
.chat-min:active { opacity: 1; }
/* unread dot — shows on the folded tab when a new message arrived */
.chat-unread {
  display: none; width: 8px; height: 8px; border-radius: 50%;
  background: #e8554e; box-shadow: 0 0 0 2px #fff8ea;
}
#chat-box.chat-collapsed.has-unread .chat-unread { display: inline-block; }
/* folded: shrink to just the header tab */
#chat-box.chat-collapsed { width: auto; gap: 0; }
#chat-box.chat-collapsed #chat-log,
#chat-box.chat-collapsed #chat-input-row { display: none; }

#chat-log {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 132px; overflow-y: auto; overflow-x: hidden;
  font-weight: 600; font-size: .78rem; line-height: 1.32;
  scrollbar-width: thin; scrollbar-color: var(--wood) transparent;
  overscroll-behavior: contain;
}
#chat-log::-webkit-scrollbar { width: 6px; }
#chat-log::-webkit-scrollbar-thumb { background: rgba(138,95,51,.4); border-radius: 3px; }
.chat-line { word-break: break-word; }
.chat-line .cl-who { font-weight: 800; margin-right: 4px; }
.chat-line.cl-me .cl-who { color: var(--gold-hi); }
.chat-line.cl-them .cl-who { color: #2f7fd6; }
.chat-line.cl-sys { color: var(--ink-soft); opacity: .9; }
.chat-line.cl-sys .cl-who { color: #3a8f5e; }

#chat-input-row { display: flex; gap: 6px; }
#chat-input {
  flex: 1; min-width: 0; font: inherit; font-size: .78rem; font-weight: 600;
  border: 2px solid var(--wood); border-radius: 12px; padding: 4px 10px;
  background: #fffdf7; color: var(--ink); outline: none;
}
#chat-input:focus { border-color: var(--gold-hi); box-shadow: 0 0 0 2px rgba(255,200,80,.3); }
#chat-send {
  flex-shrink: 0; cursor: pointer; font: inherit; font-weight: 800; font-size: .76rem;
  border: 2px solid var(--wood); border-radius: 12px; padding: 0 12px;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9); color: var(--wood-dark);
}
#chat-send:active { transform: translateY(1px); }

/* ---- mobile: NO chat at all — keep the small screen clear for thumbs ---- */
body.touch-mode #chat-box { display: none !important; }

/* ---------------- Bottom dock: hotbar + shortcuts ---------------- */
#dock {
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: linear-gradient(180deg, rgba(255,250,240,.18), rgba(241,221,180,.18));
  padding: 8px 14px; border-radius: 22px;
  border: 3px solid rgba(138,95,51,.25);
  box-shadow: inset 0 0 0 2px rgba(255,248,234,.25), 0 6px 14px rgba(120,85,45,.12);
  flex-wrap: wrap;
}
.dock-divider { width: 3px; align-self: stretch; margin: 2px 0; background: var(--wood); border-radius: 3px; opacity: .55; }

#toolbar { display: flex; gap: 8px; flex-wrap: nowrap; }
.tool-btn {
  position: relative; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 2.5px solid var(--wood); border-radius: 50%; cursor: pointer;
  width: 46px; height: 46px; padding: 0; transition: transform .1s ease, filter .15s;
  box-shadow: 0 3px 0 rgba(138,95,51,.25);
}
.tool-btn .ic { width: 1.45rem; height: 1.45rem; color: var(--wood-dark); }
#dock .tool-btn label { display: none; }
.tool-btn:hover { filter: brightness(1.04); transform: translateY(-1px); }
.tool-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(138,95,51,.25); }
.tool-btn:focus { outline: none; }
.tool-btn .tk {
  position: absolute; bottom: -14px; left: 50%; transform: translateX(-50%);
  font-size: .56rem; font-weight: 800; line-height: 1; color: var(--ink-soft); pointer-events: none; letter-spacing: .3px;
}
.tool-btn .notif {
  position: absolute; top: -4px; right: -4px; background: linear-gradient(#ff9a8d, #f06d6d); color: #fff;
  font-size: .68rem; font-weight: 800; min-width: 18px; height: 18px; border-radius: 999px;
  display: flex; align-items: center; justify-content: center; padding: 0 4px;
  border: 2px solid #fff8ea; box-shadow: 0 2px 4px rgba(120,85,45,.3);
}

/* Collapse chevron for #toolbar — touch only, sits last (rightmost) in the
   row so its screen position holds still while the other icons hide/show
   (mirrors #nav-collapse on the left side-nav). */
#toolbar-collapse {
  align-self: center; width: 28px; height: 28px; flex: none; padding: 0;
  display: none; align-items: center; justify-content: center;
  background: linear-gradient(#fffaf0, #f6e1b9); border: 2px solid var(--wood); border-radius: 50%;
  color: var(--wood-dark); font-weight: 800; cursor: pointer; line-height: 1;
  box-shadow: 0 2px 0 rgba(138,95,51,.28); transition: transform .25s;
}
body.touch-mode #toolbar-collapse { display: flex; }
#toolbar.collapsed .tool-btn { display: none; }
#toolbar.collapsed #toolbar-collapse { transform: rotate(180deg); }

/* ---------------- โหมดอยู่ในบ้าน (home mode) ----------------
   Indoors there is nothing to fight and nowhere to walk to, so everything the
   dock carries for the island comes off and one labelled row takes its place.
   See UI.setHomeMode — it only ever adds/removes .home-mode on <body>. */
body.home-mode #skillbar,
body.home-mode #hotbar,
body.home-mode #weap-lock,
body.home-mode #dock .dock-divider,
body.home-mode #toolbar,
body.home-mode #side-nav,
body.home-mode #quest-tracker,
body.home-mode #minimap,
body.home-mode #dash-btn,
body.home-mode #playdead-btn,
body.home-mode #guard-btn,
body.home-mode #jump-btn { display: none !important; }

#home-bar { display: flex; align-items: center; gap: 10px; }
#home-bar.hidden { display: none; }
.home-btn {
  position: relative; display: flex; align-items: center; gap: 8px;
  padding: 9px 16px; cursor: pointer;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 2.5px solid var(--wood); border-radius: 16px;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 4px 0 rgba(138,95,51,.28), 0 6px 12px rgba(120,85,45,.18);
  transition: transform .1s ease, filter .15s;
}
.home-btn .ic { width: 1.5rem; height: 1.5rem; color: var(--wood-dark); }
.home-btn label { font-size: .82rem; font-weight: 800; color: var(--ink); cursor: pointer; white-space: nowrap; }
.home-btn:hover { filter: brightness(1.04); transform: translateY(-1px); }
.home-btn:active { transform: translateY(3px); box-shadow: inset 0 0 0 2px #fff8ea, 0 1px 0 rgba(138,95,51,.28); }
.home-btn.slim { padding: 9px 11px; }
.home-btn.hidden { display: none; }
/* แต่งบ้าน is what you came in here for — it reads as the primary action */
.home-btn.primary { background: linear-gradient(180deg, #ffd271, #f5a73f); border-color: var(--wood); }
.home-btn.primary .ic { color: #6b4210; }
.home-btn.primary label { color: #5a3608; }
/* …and it pulses until the player has opened the catalogue once (localStorage) */
.home-btn.nudge { animation: homeNudge 1.5s ease-in-out infinite; }
@keyframes homeNudge {
  0%, 100% { box-shadow: inset 0 0 0 2px #fff8ea, 0 4px 0 rgba(138,95,51,.28), 0 0 0 0 rgba(245,167,63,.55); }
  55%      { box-shadow: inset 0 0 0 2px #fff8ea, 0 4px 0 rgba(138,95,51,.28), 0 0 0 9px rgba(245,167,63,0); }
}
/* decorate mode engaged — same gold "pressed in" language as an assigned hotbar slot */
.home-btn.active {
  border-color: var(--gold); box-shadow: 0 0 0 2px var(--gold), inset 0 1px 0 #fff8ea, 0 2px 8px rgba(245,167,63,.4);
}
.home-btn.active .ic { color: #b5790a; }
/* phones: the labels are the whole point of this bar, so shrink rather than drop them */
body.touch-mode .home-btn { padding: 7px 11px; gap: 6px; border-radius: 13px; }
body.touch-mode .home-btn .ic { width: 1.25rem; height: 1.25rem; }
body.touch-mode .home-btn label { font-size: .7rem; }
/* ปรับปรุงบ้าน's only nudge: a quiet dot once the next rung is affordable.
   Deliberately NOT the pulsing .nudge the furniture button uses — this one has
   to be findable, not sold (see UI.syncHutButton). */
.home-btn .hb-dot {
  position: absolute; top: -3px; right: -3px; width: 11px; height: 11px; border-radius: 50%;
  background: var(--good); border: 2px solid #fff8ea; box-shadow: 0 1px 3px rgba(120,85,45,.4);
}
.home-btn .hb-dot.hidden { display: none; }

/* ---------------- บ้าน: the upgrade ladder panel (UI._renderHut) ----------
   Reads as a record of the house, not a store: what it already gives you, one
   line saying none of it is required, the single actionable rung, then the
   remaining rungs greyed so the road has a shape without becoming a to-do list. */
.hut-head {
  display: flex; align-items: center; gap: 14px; padding: 10px 12px; margin-bottom: 10px;
  background: linear-gradient(180deg, #fffaf0, #faecca);
  border: 2px solid var(--wood); border-radius: 18px; box-shadow: 0 3px 0 rgba(138,95,51,.16);
}
.hut-head-art { width: 62px; height: 62px; object-fit: contain; flex-shrink: 0; image-rendering: auto; }
.hut-head-name { font-weight: 900; font-size: 1.02rem; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.hut-lv-chip {
  font-size: .74rem; font-weight: 900; color: #6b4210; padding: 2px 9px; border-radius: 999px;
  background: linear-gradient(180deg, #ffd271, #f5a73f); border: 1.5px solid var(--wood);
}
.hut-head-holds { font-size: .8rem; color: var(--ink-soft); margin-top: 4px; line-height: 1.5; }
.hut-head-holds b { color: var(--ink); }
.hut-optional { font-size: .78rem; color: var(--ink-soft); margin: 0 4px 12px; font-style: italic; }

.hut-rung {
  background: linear-gradient(180deg, #fffaf0, #faecca);
  border: 2px solid var(--wood); border-radius: 18px; padding: 11px 13px; margin-bottom: 10px;
  box-shadow: 0 3px 0 rgba(138,95,51,.16);
}
.hut-rung-name { font-weight: 900; color: var(--ink); }
.hut-rung-label { font-size: .72rem; font-weight: 800; color: var(--ink-soft); margin-top: 9px; text-transform: uppercase; letter-spacing: .04em; }
/* cost pills — same red/green read as the backpack upgrade row, but roomy
   enough that the baked icon is legible next to the have/need count */
.hut-rung .r-cost { display: flex; flex-wrap: wrap; gap: 6px; font-size: .84rem; margin-top: 5px; }
.hut-rung .r-cost span {
  display: inline-flex; align-items: center; gap: 5px; font-weight: 800; white-space: nowrap;
  padding: 4px 11px; border-radius: 999px; background: rgba(224,70,59,.1); color: #d2463b;
}
.hut-rung .r-cost span.ok { background: rgba(74,156,62,.12); color: #4a9c3e; }
.hut-rung .r-cost .ic-sm { width: 1.5em; height: 1.5em; vertical-align: 0; }
.hut-perk { display: flex; align-items: center; gap: 9px; font-size: .85rem; color: var(--ink); margin-top: 6px; }
.hut-perk-ic { width: 1.2rem; height: 1.2rem; color: var(--wood-dark); flex-shrink: 0; }
.hut-perk .ic-sm { width: 1.45em; height: 1.45em; vertical-align: 0; flex-shrink: 0; }
.hut-up-btn { display: block; width: 100%; margin-top: 12px; }
/* .craft-btn is styled under .recipe — repeat the two rules the button needs here */
.hut-up-btn {
  background: linear-gradient(#a4dba0, #7bc47f); color: #234a25; border: 2px solid #d3f0d0; border-radius: 999px;
  font-weight: 800; padding: 10px 16px; cursor: pointer; box-shadow: 0 3px 0 #5a9e5e; font-family: var(--font); font-size: .92rem;
}
.hut-up-btn:disabled { background: #e3d4b6; box-shadow: 0 3px 0 #c9b68f; cursor: not-allowed; color: #a08b66; border-color: #f3e7cd; }
.hut-rung.far { opacity: .5; box-shadow: none; padding: 8px 13px; }
.hut-rung.far .hut-rung-name { font-weight: 800; font-size: .88rem; }
.hut-rung-need { font-size: .74rem; color: var(--ink-soft); margin-top: 2px; }
.hut-rung.maxed { text-align: center; }
/* landscape phones have ~390px of height: the header must not push the one
   rung you can act on below the fold, so it compacts instead of scrolling */
@media (orientation: landscape) and (max-height: 520px) {
  .hut-head { gap: 10px; padding: 7px 10px; margin-bottom: 7px; }
  .hut-head-art { width: 42px; height: 42px; }
  .hut-head-name { font-size: .92rem; }
  .hut-head-holds { font-size: .72rem; margin-top: 2px; }
  .hut-optional { font-size: .7rem; margin-bottom: 8px; }
  .hut-rung { padding: 9px 11px; margin-bottom: 7px; }
  .hut-rung-label { margin-top: 6px; }
  .hut-perk { margin-top: 4px; font-size: .8rem; }
  .hut-up-btn { margin-top: 9px; padding: 8px 14px; }
}

/* ---------------- Bottom-right: big action button ---------------- */
#action-btn {
  position: absolute; right: 22px; bottom: 18px; width: 88px; height: 88px;
  display: none; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  background: radial-gradient(circle at 50% 35%, #ffd271, #f5a73f);
  border: 4px solid #fff8ea; border-radius: 50%; cursor: pointer;
  box-shadow: 0 0 0 3px var(--wood), 0 6px 14px rgba(120,85,45,.3);
  transition: transform .1s ease, filter .15s;
}
#action-btn:hover { filter: brightness(1.05); }
#action-btn:active { transform: translateY(3px) scale(.97); }
.ab-icon { width: 2.3rem; height: 2.3rem; color: #6e4310; filter: drop-shadow(0 1px 2px rgba(255,255,255,.5)); }
.ab-label { font-size: .6rem; font-weight: 800; color: #7a4f1f; letter-spacing: .5px; text-transform: uppercase; }

/* "Move" button — the touch counterpart of the desktop #move-hint; sits just
   above the action button whenever a built structure is in reach. Touch only. */
#move-btn {
  position: absolute; right: 30px; bottom: 116px; width: 64px; height: 64px;
  display: none; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: radial-gradient(circle at 50% 35%, #bfe3ff, #6fb6ee);
  border: 4px solid #fff8ea; border-radius: 50%; cursor: pointer; z-index: 31;
  box-shadow: 0 0 0 3px var(--wood), 0 6px 14px rgba(45,85,120,.3);
  transition: transform .1s ease, filter .15s;
}
body.touch-mode #move-btn { display: flex; }
#move-btn:hover { filter: brightness(1.05); }
#move-btn:active { transform: translateY(3px) scale(.97); }
.mb-icon { width: 1.7rem; height: 1.7rem; color: #16455f; filter: drop-shadow(0 1px 2px rgba(255,255,255,.5)); }
.mb-label { font-size: .55rem; font-weight: 800; color: #1d4f6e; letter-spacing: .5px; text-transform: uppercase; }
/* on touch the move button stacks above the dash button (the slot above the
   action button belongs to the jump button) */
body.touch-mode #move-btn {
  right: calc(124px + env(safe-area-inset-right));
  bottom: calc(214px + env(safe-area-inset-bottom));
}

/* "Cook" button — the touch counterpart of the desktop #cook-hint; stacks
   directly above the move button (same column) whenever a lit campfire is in
   reach. Warm amber palette (vs. move's blue) so the two never get mixed up. */
#cook-btn {
  position: absolute; right: 30px; bottom: 116px; width: 56px; height: 56px;
  display: none; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: radial-gradient(circle at 50% 35%, #ffe0a3, #f5a73f);
  border: 4px solid #fff8ea; border-radius: 50%; cursor: pointer; z-index: 31;
  box-shadow: 0 0 0 3px var(--wood), 0 6px 14px rgba(120,75,20,.3);
  transition: transform .1s ease, filter .15s;
}
body.touch-mode #cook-btn { display: flex; }
/* touch: the button rides ON the campfire (UI.showCookButton writes left/top in
   screen space each frame) instead of joining the thumb-side stack, which is
   already crowded with action/dash/jump/skills. `left/top` are the fire's spot,
   so translate back by half to centre the button on it. */
body.touch-mode #cook-btn.at-fire {
  right: auto; bottom: auto;
  transform: translate(-50%, -50%);
  width: 52px; height: 52px; z-index: 24;   /* under the toasts/panels, over the world */
  animation: cookFireBob 1.6s ease-in-out infinite;
}
body.touch-mode #cook-btn.at-fire:active { transform: translate(-50%, -50%) scale(.92); animation: none; }
body.touch-mode #cook-btn.at-fire .cb-label { display: none; }   /* the fire beneath it says what it is */
body.touch-mode #cook-btn.at-fire .cb-icon { width: 1.7rem; height: 1.7rem; }
@keyframes cookFireBob {
  0%, 100% { box-shadow: 0 0 0 3px var(--wood), 0 6px 14px rgba(120,75,20,.3), 0 0 0 0 rgba(245,167,63,.55); }
  50%      { box-shadow: 0 0 0 3px var(--wood), 0 6px 14px rgba(120,75,20,.3), 0 0 0 9px rgba(245,167,63,0); }
}
#cook-btn:hover { filter: brightness(1.05); }
#cook-btn:active { transform: translateY(3px) scale(.97); }
.cb-icon { width: 1.5rem; height: 1.5rem; color: #6e4310; filter: drop-shadow(0 1px 2px rgba(255,255,255,.5)); }
.cb-label { font-size: .5rem; font-weight: 800; color: #7a4f1f; letter-spacing: .5px; text-transform: uppercase; }

/* "Dash" button — touch counterpart of the Shift key */
#dash-btn {
  position: absolute; right: 124px; bottom: 24px; width: 58px; height: 58px;
  display: none; align-items: center; justify-content: center;
  background: rgba(31,22,12,.34);
  border: 1.5px solid rgba(255,239,210,.32); border-radius: 50%; cursor: pointer; z-index: 31;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 3px 10px rgba(0,0,0,.22);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  transition: transform .08s ease, background .12s, border-color .12s;
}
body.touch-mode #dash-btn { display: flex; }
#dash-btn:active { transform: scale(.9); background: rgba(245,167,63,.32); border-color: rgba(255,239,210,.6); }
/* combat onboarding — the first enemy hit pulses this button so the player
   discovers the dodge (see Game._maybeTeachDodge); cleared on the first roll. */
#dash-btn.db-teach { border-color: #6fd3ff; animation: dbTeachPulse 1s ease-in-out infinite; }
@keyframes dbTeachPulse {
  0%, 100% { box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 0 0 0 rgba(111,211,255,.7); }
  50%      { box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 0 0 8px rgba(111,211,255,0), 0 0 16px rgba(111,211,255,.95); }
}
.db-icon { width: 1.55rem; height: 1.55rem; color: rgba(255,247,232,.9); }
/* match the action button's raised touch position (its bottom is 118px + safe-area) */
body.touch-mode #dash-btn {
  right: calc(124px + env(safe-area-inset-right));
  bottom: calc(118px + env(safe-area-inset-bottom));
}

/* "Jump" button — touch counterpart of the Space key; same glass-circle
   language as #dash-btn, tucked diagonally up-right of the action button */
#jump-btn {
  position: absolute; right: 12px; bottom: 214px; width: 52px; height: 52px;
  display: none; align-items: center; justify-content: center;
  background: rgba(31,22,12,.34);
  border: 1.5px solid rgba(255,239,210,.32); border-radius: 50%; cursor: pointer; z-index: 31;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 3px 10px rgba(0,0,0,.22);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  transition: transform .08s ease, background .12s, border-color .12s;
}
#jump-btn:active { transform: scale(.9); background: rgba(245,167,63,.32); border-color: rgba(255,239,210,.6); }
.jb-icon { width: 1.55rem; height: 1.55rem; color: rgba(255,247,232,.9); }
body.touch-mode #jump-btn {
  display: flex;
  right: calc(12px + env(safe-area-inset-right));
  bottom: calc(214px + env(safe-area-inset-bottom));
}

/* "Play dead" button — touch counterpart of the H key; same glass circle as
   #dash-btn, stacked directly above it. Goes cyan while the flop is live (tap
   again to spring up) and dims out during its cooldown. */
#playdead-btn {
  position: absolute; right: 124px; bottom: 90px; width: 52px; height: 52px;
  display: none; align-items: center; justify-content: center;
  background: rgba(31,22,12,.34);
  border: 1.5px solid rgba(255,239,210,.32); border-radius: 50%; cursor: pointer; z-index: 31;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 3px 10px rgba(0,0,0,.22);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  transition: transform .08s ease, background .12s, border-color .12s, opacity .12s;
}
body.touch-mode #playdead-btn { display: flex; }
#playdead-btn:active { transform: scale(.9); background: rgba(245,167,63,.32); border-color: rgba(255,239,210,.6); }
#playdead-btn.pd-live { background: rgba(111,211,255,.34); border-color: rgba(159,232,255,.75); }
#playdead-btn.pd-cool { opacity: .38; }
.pd-icon { width: 1.5rem; height: 1.5rem; color: rgba(255,247,232,.9); }
body.touch-mode #playdead-btn {
  right: calc(124px + env(safe-area-inset-right));
  bottom: calc(184px + env(safe-area-inset-bottom));
}

/* อารมณ์ — the emote wheel (hold a press on your own castaway). The element
   itself is a full-screen veil that swallows the tap that closes it, so a
   dismiss can never leak through as a swing; the ring inside is placed over
   the castaway's head. Same glass-circle language as the touch buttons. */
#emote-wheel {
  position: fixed; inset: 0; z-index: 115;   /* over the HUD + toasts (110), under the full-screen modals */
  -webkit-tap-highlight-color: transparent;
}
#emote-wheel.hidden { display: none; }
.ew-ring { position: absolute; width: 0; height: 0; }
/* the prompt sits ABOVE the ring, not in the middle of it — the middle is
   where the castaway is standing, and covering them defeats the whole gesture */
.ew-hub {
  position: absolute; left: 50%; top: calc(-1 * var(--r, 92px) - 44px); transform: translate(-50%, -50%);
  padding: 7px 12px; border-radius: 14px; white-space: nowrap;
  font-size: .72rem; font-weight: 800; color: rgba(255,247,232,.92);
  background: rgba(31,22,12,.5); border: 1.5px solid rgba(255,239,210,.22);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  pointer-events: none;
}
.ew-item {
  position: absolute; left: 50%; top: 50%;
  width: 56px; height: 56px; padding: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: rgba(31,22,12,.42);
  border: 1.5px solid rgba(255,239,210,.3); border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 3px 10px rgba(0,0,0,.28);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  animation: ew-pop .16s ease-out both;
  transition: background .1s, border-color .1s, box-shadow .1s;
}
/* --emo is the feeling's own colour, set inline per slot from GameData.EMOTES */
.ew-item .ew-ic { width: 1.55rem; height: 1.55rem; color: var(--emo, #ffe7bd); pointer-events: none; }
.ew-item .ew-ic svg { width: 100%; height: 100%; display: block; }
.ew-item .ew-lb {
  font-size: .56rem; font-weight: 800; letter-spacing: .2px; line-height: 1;
  color: rgba(255,247,232,.78); pointer-events: none; max-width: 54px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ew-item.on, .ew-item:hover {
  background: rgba(76,54,28,.74);
  border-color: var(--emo, #ffe7bd);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.1), 0 0 16px -2px var(--emo, #ffe7bd);
}
.ew-item.on .ew-lb { color: rgba(255,252,244,.96); }
@keyframes ew-pop { from { opacity: 0; } to { opacity: 1; } }

/* "Guard / Parry" button — touch counterpart of the right mouse button. Same
   glass circle as #dash-btn, stacked above the play-dead skull. HELD, not
   tapped: it goes gold while the shield is up (the perfect window is the first
   beat of that hold) and dims through the cooldown / the broken-guard stagger.
   Shown only while a shield is actually worn — see UI._tickGuardBtn. */
#guard-btn {
  position: absolute; right: 124px; bottom: 156px; width: 52px; height: 52px;
  display: none; align-items: center; justify-content: center;
  background: rgba(31,22,12,.34);
  border: 1.5px solid rgba(255,239,210,.32); border-radius: 50%; cursor: pointer; z-index: 31;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 3px 10px rgba(0,0,0,.22);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  transition: transform .08s ease, background .12s, border-color .12s, opacity .12s;
}
body.touch-mode #guard-btn { display: flex; }
#guard-btn:active { transform: scale(.92); }
#guard-btn.gb-live { background: rgba(245,205,120,.38); border-color: rgba(255,233,168,.8); transform: scale(.94); }
#guard-btn.gb-cool { opacity: .38; }
/* combat onboarding — the first hit taken while wearing a shield pulses this
   button (see Game._maybeTeachParry); cleared the first time the guard goes up */
#guard-btn.gb-teach { border-color: #ffe9a8; animation: dbTeachPulse 1s ease-in-out infinite; }
.gb-icon { width: 1.5rem; height: 1.5rem; color: rgba(255,247,232,.9); }
body.touch-mode #guard-btn {
  right: calc(124px + env(safe-area-inset-right));
  bottom: calc(250px + env(safe-area-inset-bottom));
}

/* ---------------- Inventory / grids ---------------- */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr)); gap: 10px; }
.cell {
  background: linear-gradient(180deg, #fffaf0, #faecca); border: 2px solid var(--wood); border-radius: 16px; padding: 8px 4px; text-align: center;
  position: relative; box-shadow: 0 3px 0 rgba(138,95,51,.18); cursor: default;
}
.cell .emoji { font-size: 1.8rem; line-height: 1; }
.cell .qty { position: absolute; bottom: 3px; right: 6px; font-weight: 800; font-size: .8rem; color: var(--gold-hi); }
.cell .cname { font-size: .66rem; margin-top: 3px; color: var(--ink-soft); font-weight: 700; }
.empty-note { color: var(--ink-soft); opacity: .85; text-align: center; padding: 20px; font-style: italic; }

/* recipe / build rows */
.recipe {
  display: flex; align-items: center; gap: 12px; background: linear-gradient(180deg, #fffaf0, #faecca);
  border: 2px solid var(--wood); border-radius: 18px; padding: 10px 12px; margin-bottom: 10px; box-shadow: 0 3px 0 rgba(138,95,51,.16);
}
.recipe.recipe-highlight {
  border-color: var(--gold); animation: recipeFlash 2s ease;
  box-shadow: 0 3px 0 rgba(138,95,51,.16), 0 0 18px rgba(234,165,62,.45);
}
@keyframes recipeFlash {
  0%,100% { box-shadow: 0 3px 0 rgba(138,95,51,.16), 0 0 18px rgba(234,165,62,.45); }
  30%     { box-shadow: 0 3px 0 rgba(138,95,51,.16), 0 0 36px rgba(234,165,62,.8); }
}
.recipe .r-icon { font-size: 2.1rem; width: 2.4rem; text-align: center; flex-shrink: 0; }
.recipe canvas.r-icon { width: 48px; height: 52px; border-radius: 8px; display: block; }
.recipe svg.r-icon { width: 2rem; height: 2rem; flex-shrink: 0; }
.recipe .r-icon-empty { font-size: 1.4rem; width: 2.4rem; text-align: center; line-height: 52px; color: var(--ink-soft); flex-shrink: 0; }
.recipe .r-info { flex: 1; min-width: 0; }
.recipe .r-name { font-weight: 800; color: var(--ink); }
.recipe .r-desc { font-size: .78rem; color: var(--ink-soft); }
.recipe .vol-pct { font-weight: 800; color: var(--ink-soft); font-variant-numeric: tabular-nums; min-width: 3.2em; text-align: right; flex-shrink: 0; }
.vol-slider {
  -webkit-appearance: none; appearance: none; width: 100%; height: 8px; margin-top: 8px;
  border-radius: 999px; background: linear-gradient(90deg, var(--gold), #f0d28a); border: 2px solid var(--wood); cursor: pointer;
}
.vol-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 22px; height: 22px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fff, #e7a93e); border: 2px solid var(--wood); box-shadow: 0 2px 0 rgba(138,95,51,.4); cursor: pointer;
}
.vol-slider::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fff, #e7a93e); border: 2px solid var(--wood); box-shadow: 0 2px 0 rgba(138,95,51,.4); cursor: pointer;
}
.recipe .r-cost { font-size: .82rem; margin-top: 3px; }
.recipe .r-cost .miss { color: var(--bad); font-weight: 800; }
.recipe .r-cost .ok { color: var(--leaf-dark); font-weight: 700; }
/* ---- the รายละเอียด switch ------------------------------------------------
   One class on <body> collapses every verbose block back to the pre-hint
   panels. Done in CSS so the flip is instant and never rebuilds a panel
   mid-tap (a rebuild eats the tap — see the panel-tap-eaten note). */
body.hints-off .r-gs,
body.hints-off .r-src,
body.hints-off .r-up,
body.hints-off .invd-src,
body.hints-off .qt-src { display: none; }
/* the switch itself: an outline icon-button that fills in when detail is ON,
   so its state is readable without a label in either panel header */
.hint-toggle { font-size: .9rem; opacity: .55; }
.hint-toggle.on { opacity: 1; color: var(--gold-hi); background: rgba(240, 165, 42, .16); }

/* the gear stat row — "is this better than what I'm holding?" answered inline.
   The ▲/▼ delta is the part the eye should land on, so it is the only coloured
   thing in the row; the absolute numbers stay quiet. */
.r-gs { display: flex; flex-wrap: wrap; gap: 4px 8px; margin-top: 3px; font-size: .74rem; font-weight: 800; color: var(--ink-soft); }
.r-gs .gs { white-space: nowrap; }
.r-gs .gs-cls { padding: 0 6px; border-radius: 999px; background: rgba(0, 0, 0, .06); font-size: .68rem; font-weight: 800; }
.r-gs .gs-up { font-style: normal; color: var(--leaf-dark); }
.r-gs .gs-dn { font-style: normal; color: var(--bad); }
/* "ตีต่อยอดเป็น": what this item is a COMPONENT of. Same quiet note styling as
   the where-to-find block — both are directions, they just point opposite ways. */
.r-up { margin-top: 5px; padding: 5px 8px; border-radius: 9px; background: rgba(0, 0, 0, .045); border-left: 3px solid var(--leaf-dark); }
.r-up-h { display: block; font-size: .64rem; font-weight: 800; letter-spacing: .3px; color: var(--leaf-dark); margin-bottom: 2px; }
.r-up-row { font-size: .72rem; font-weight: 600; color: var(--ink-soft); line-height: 1.45; }
.r-up-row b { color: var(--ink); font-weight: 800; }
.r-up-row .ic-sm, .r-up-cost .ic-sm { vertical-align: -2px; }
.r-up-cost { opacity: .85; }

/* "ยังขาดอยู่ — หาได้จาก": the where-to-find block under an unaffordable
   recipe. Deliberately quiet (a tinted note, not a warning) — it's directions,
   not an error, and it must never out-shout the recipe's own name. */
.recipe .r-src {
  margin-top: 5px; padding: 5px 8px; border-radius: 9px;
  background: rgba(0, 0, 0, .045); border-left: 3px solid var(--gold-lo);
}
.recipe .r-src-h { display: block; font-size: .64rem; font-weight: 800; letter-spacing: .3px; color: var(--gold-hi); margin-bottom: 2px; }
.recipe .r-src-row { font-size: .72rem; font-weight: 600; color: var(--ink-soft); line-height: 1.45; }
.recipe .r-src-row b { color: var(--ink); font-weight: 800; }
.recipe .r-src-row .ic-sm { vertical-align: -2px; }
.recipe .r-nutri { font-size: .78rem; font-weight: 700; color: var(--leaf-dark); margin-top: 2px; }
.recipe .r-nutri .neg { color: var(--bad); }
.recipe .craft-btn {
  background: linear-gradient(#a4dba0, #7bc47f); color: #234a25; border: 2px solid #d3f0d0; border-radius: 999px;
  font-weight: 800; padding: 9px 16px; cursor: pointer; box-shadow: 0 3px 0 #5a9e5e; font-family: var(--font);
}
.recipe .craft-btn:disabled { background: #e3d4b6; box-shadow: 0 3px 0 #c9b68f; cursor: not-allowed; color: #a08b66; border-color: #f3e7cd; }
/* ×N batch stepper: [−] ×N [+] tucked between the recipe info and the Make button */
.recipe .craft-qty { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.recipe .craft-qty .qty-btn {
  width: 30px; height: 30px; padding: 0; border-radius: 999px; border: 2px solid var(--wood);
  background: linear-gradient(#fffaf0, #f3e2bd); color: var(--ink); font-weight: 900; font-size: 1.15rem;
  line-height: 1; cursor: pointer; box-shadow: 0 2px 0 rgba(138,95,51,.28); font-family: var(--font); flex-shrink: 0;
}
.recipe .craft-qty .qty-btn:disabled { opacity: .4; cursor: not-allowed; box-shadow: none; }
.recipe .craft-qty .qty-btn:not(:disabled):active { transform: translateY(2px); box-shadow: none; }
.recipe .craft-qty .qty-n {
  min-width: 2.4em; text-align: center; font-weight: 900; color: var(--gold-hi);
  font-variant-numeric: tabular-nums; cursor: pointer; user-select: none;
}
.recipe .craft-btn:not(:disabled):active { transform: translateY(2px); }
.recipe .craft-btn.off { background: #cdbf9f; box-shadow: 0 3px 0 #ab9b76; color: #6b5a3a; border-color: #e6d8ba; }
.recipe.owned { opacity: .6; }
.station-tag { font-size: .72rem; font-weight: 800; color: var(--gold-hi); }
.section-title { font-weight: 800; color: var(--gold-hi); margin: 4px 0 10px; font-size: 1.05rem; }

/* a recipe the tracked quest is asking for — gold frame; once its materials are
   in, it turns green with a glow and its button pulses so "make it!" is obvious.
   These reinforce the tracker CTA the moment the player lands on the panel. */
.recipe.quest-goal { border-color: var(--gold); box-shadow: 0 3px 0 rgba(138,95,51,.16), 0 0 0 1px var(--gold-lo) inset; }
.recipe.quest-ready { border-color: var(--leaf-dark); box-shadow: 0 3px 0 rgba(90,158,94,.28), 0 0 16px rgba(123,196,127,.5); }
.quest-goal-badge {
  display: inline-block; margin-left: 7px; font-size: .64rem; font-weight: 800; vertical-align: middle;
  color: var(--gold-hi); background: linear-gradient(#ffe19a, var(--gold)); border: 1.5px solid var(--gold-hi);
  border-radius: 999px; padding: 1px 8px; letter-spacing: .2px;
}
.quest-goal-badge.rdy { color: #234a25; background: linear-gradient(#a4dba0, var(--leaf)); border-color: var(--leaf-dark); }
.recipe .craft-btn.craft-pulse { animation: craftBtnPulse 1.1s ease-in-out infinite; }
@keyframes craftBtnPulse {
  0%,100% { box-shadow: 0 3px 0 var(--leaf-dark); transform: scale(1); }
  50%     { box-shadow: 0 3px 0 var(--leaf-dark), 0 0 14px rgba(123,196,127,.75); transform: scale(1.04); }
}
/* dot on the category tab that holds the active quest's craft goal */
.craft-tab-dot {
  display: inline-block; width: 8px; height: 8px; margin-left: 5px; border-radius: 50%;
  background: var(--gold); vertical-align: middle;
}
.craft-tab-dot.rdy { background: var(--leaf-dark); animation: craftDotPulse 1.1s ease-in-out infinite; }
@keyframes craftDotPulse { 0%,100% { box-shadow: 0 0 0 rgba(123,196,127,0); } 50% { box-shadow: 0 0 8px rgba(123,196,127,.9); } }

/* character / equipment panel */
/* ---- Character panel: two panes — fixed stage on top, scrolling drawer ---- */
.panel.panel-tall { height: 86vh; }
#panel-body.pb-flush { display: flex; flex-direction: column; overflow: hidden; padding: 0; }
.char-screen { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.char-stage {
  flex: 0 0 auto; padding: 16px 20px 14px;
  background: linear-gradient(180deg, #fdf3df, #fbeccb);
  border-bottom: 3px solid var(--wood);
  box-shadow: 0 6px 12px -6px rgba(120,85,45,.35);
}
.char-drawer { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px 20px 18px; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.cs-top { display: flex; gap: 12px; align-items: center; }
.char-portrait { display: flex; justify-content: center; margin-bottom: 10px; }
.char-portrait canvas {
  background: radial-gradient(circle at 50% 38%, #fffaf0, #f6e1b9);
  border: 3px solid var(--wood); border-radius: 20px; box-shadow: 0 4px 0 rgba(138,95,51,.2);
}
.char-stage .char-portrait { margin: 0; flex-shrink: 0; }
.char-stage .char-portrait canvas { width: 132px; height: 152px; }
.cs-slots { display: flex; flex-direction: column; gap: 7px; flex: 1; min-width: 0; }
.eq-slot {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: #fff8ea; border: 2.5px solid var(--gold-lo); border-radius: 14px;
  padding: 5px 9px; font: inherit; text-align: left; color: var(--ink);
}
.eq-slot.filled { cursor: pointer; box-shadow: inset 0 0 0 2px var(--leaf); }
.eq-slot.filled:active { transform: translateY(1px); }
.eq-slot.empty { border-style: dashed; opacity: .75; }
.eq-cv { width: 36px; height: 40px; flex-shrink: 0; }
.eq-plus { width: 36px; text-align: center; font-size: 1.3rem; font-weight: 800; color: var(--ink-soft); flex-shrink: 0; }
.eq-text { display: flex; flex-direction: column; flex: 1; min-width: 0; line-height: 1.15; }
.eq-slotname { font-size: .6rem; font-weight: 800; color: var(--gold-hi); text-transform: uppercase; letter-spacing: .3px; }
.eq-name { font-size: .82rem; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.eq-name.eq-empty { color: var(--ink-soft); font-weight: 600; }
.eq-buff { font-size: .68rem; font-weight: 700; color: var(--leaf); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.eq-x { font-size: .72rem; font-weight: 900; color: var(--bad); flex-shrink: 0; }
.cs-meta { margin-top: 12px; }
/* Desktop web: same side-by-side split as mobile landscape — stacked, the
   fixed stage eats most of the 86vh panel and the wardrobe drawer becomes a
   short strip that's a pain to scroll. Widen the panel and put the stage as
   a fixed left column with the drawer scrolling on the right instead.
   (touch-mode gets its own version in the landscape block further down.) */
@media (min-width: 880px) {
  body:not(.touch-mode) .panel.panel-tall { max-width: 880px; height: min(640px, 86vh); }
  body:not(.touch-mode) .char-screen { flex-direction: row; }
  body:not(.touch-mode) .char-stage {
    flex: 0 0 356px; min-height: 0; overflow-y: auto;
    border-bottom: none; border-right: 3px solid var(--wood);
    box-shadow: 6px 0 12px -6px rgba(120,85,45,.35);
  }
  body:not(.touch-mode) .char-drawer { min-width: 0; }
}
.cs-lvl { display: flex; align-items: center; gap: 8px; font-weight: 800; font-size: .98rem; color: var(--gold-hi); margin-bottom: 6px; flex-wrap: wrap; }
.cs-stat { background: #fff8ea; border: 2px solid var(--gold-lo); border-radius: 20px; padding: 2px 9px; font-size: .76rem; color: var(--ink); }
.dr-note { text-align: left; padding: 0 0 10px !important; }
.char-card { cursor: pointer; }
.char-card.previewing { box-shadow: 0 0 0 3px var(--gold-hi), 0 3px 0 rgba(138,95,51,.16); }
.char-head { margin-bottom: 12px; }
.char-level { font-weight: 800; font-size: 1.05rem; color: var(--gold-hi); margin-bottom: 6px; }
.xp-bar { position: relative; height: 18px; border-radius: 999px; background: rgba(120,85,45,.14); overflow: hidden; box-shadow: inset 0 2px 3px rgba(120,85,45,.2); }
.xp-fill { height: 100%; width: 0; border-radius: 999px; background: linear-gradient(90deg,#ffe27a,#f5a73f); transition: width .3s ease; }
.xp-text { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: .72rem; font-weight: 800; color: #6e4310; }
.char-stats { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.char-stats span { background: #fff8ea; border: 2px solid var(--gold-lo); border-radius: 20px; padding: 4px 10px; font-size: .82rem; font-weight: 800; color: var(--ink); }
.char-sub { font-weight: 800; color: var(--gold-hi); margin: 4px 0 8px; font-size: .95rem; }
/* ---- Attributes overview (radar hexagon + chip list) on the Stats tab ---- */
.stat-overview { margin-bottom: 14px; padding-bottom: 12px; border-bottom: 2px dashed var(--wood); }
.stat-radar-wrap { display: flex; justify-content: center; margin: 2px 0 12px; }
.stat-radar { width: 100%; max-width: 260px; height: auto; overflow: visible; }
.stat-radar .rad-ab { font-size: 10px; font-weight: 800; font-family: var(--font); }
.stat-radar .rad-num { font-size: 9.5px; font-weight: 700; font-family: var(--font); fill: var(--ink); opacity: .8; }
.stat-list { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.stat-chip { display: flex; align-items: center; gap: 7px; background: #fff8ea; border: 2px solid var(--gold-lo); border-radius: 12px; padding: 6px 9px; }
.stat-chip-ic { width: 17px; height: 17px; color: var(--wood-dark); flex-shrink: 0; }
.stat-chip-name { font-size: .76rem; font-weight: 700; color: var(--ink-soft); flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stat-chip-val { font-size: .82rem; font-weight: 800; color: var(--ink); flex-shrink: 0; }
/* the 5 chips backed by a skill point (str/vit/agi/sta/end) get this inline
   + right in the summary — spend without hunting for the detailed row below. */
.stat-chip-plus {
  flex-shrink: 0; width: 21px; height: 21px; margin-left: 2px; padding: 0;
  border-radius: 50%; border: 1.5px solid #fff8ea; cursor: pointer;
  background: linear-gradient(180deg, #ff9a8d, #f06d6d); color: #fff;
  font-weight: 800; font-size: .85rem; line-height: 1; font-family: var(--font);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 0 rgba(138,95,51,.28);
}
.stat-chip-plus:active { transform: translateY(1px); box-shadow: none; }
.stat-chip-plus.off { opacity: .35; background: #cfcabf; box-shadow: none; pointer-events: none; }
/* first time the castaway goes winded, Game._maybeTeachWinded auto-opens this
   panel and pulses the Stamina + button (both here and on the detailed row
   below) so "spend a point here" has an obvious answer instead of a toast to
   notice and tap. Cleared the moment that button is pressed, or the panel
   closes without it — see UI._teachStaminaBtn. */
.skill-teach { animation: dbTeachPulse 1s ease-in-out infinite; }
/* Clothing / Weapons sub-tabs at the top of the character drawer */
.char-tabs { display: flex; gap: 8px; margin: 0 0 14px; }
/* the furniture catalogue has 8 tabs — let them wrap and shrink rather than
   squeezing every label to one illegible character on a phone */
.furn-tabs { flex-wrap: wrap; gap: 6px; }
.furn-tabs .char-tab { padding: 5px 10px; font-size: 13px; }
.char-tab {
  flex: 1; padding: 9px 12px; border-radius: 14px; font-family: var(--font); font-weight: 800; font-size: .9rem;
  cursor: pointer; color: var(--ink-soft);
  background: linear-gradient(#fffaf0, #f4e3bf); border: 2.5px solid var(--wood); box-shadow: 0 3px 0 rgba(138,95,51,.18);
  transition: filter .1s, transform .08s;
}
.char-tab:hover { filter: brightness(1.03); }
.char-tab:not(.on):active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(138,95,51,.18); }
.char-tab.on { background: linear-gradient(#ffe19a, var(--gold)); color: var(--wood-dark); border-color: var(--gold-hi); }
/* crafting has 5 category tabs — tighten them so they fit a phone; if they still
   overflow (long labels / small screen) the strip scrolls sideways rather than
   wrapping into a second cramped row. */
.craft-tabs { gap: 6px; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.craft-tabs::-webkit-scrollbar { display: none; }
.craft-tabs .char-tab { flex: 1 0 auto; padding: 8px 10px; font-size: .82rem; white-space: nowrap; }
.recipe.equipped { box-shadow: 0 3px 0 rgba(138,95,51,.16), inset 0 0 0 2.5px var(--leaf); }
.recipe.slot-empty { opacity: .6; }
.recipe .craft-btn.off { background: linear-gradient(#ff9a8d,#f06d6d); box-shadow: 0 3px 0 #c44b41; color: #fff; border-color: #ffd3cc; }
.slot-tag { font-size: .68rem; font-weight: 800; color: var(--gold-hi); text-transform: uppercase; margin-left: 4px; }
.mat-bar { display: flex; flex-wrap: wrap; gap: 5px; padding-bottom: 12px; margin-bottom: 12px; border-bottom: 2px dashed var(--wood); }
.mat-chip { display: flex; align-items: center; gap: 3px; background: #fff8ea; border: 2px solid var(--gold-lo); border-radius: 20px; padding: 3px 9px; font-size: .8rem; font-weight: 700; color: var(--ink); }
.mat-chip .m-icon { font-size: 1rem; line-height: 1; }
.mat-chip .m-qty { color: var(--ink-soft); }

/* ---- skill points (Stats tab + portrait call-to-action) ---- */
.portrait-frame { cursor: pointer; }
.portrait-frame:active { transform: scale(.97); }
.portrait-frame .pts-badge {
  position: absolute; bottom: -3px; right: -5px; min-width: 22px; height: 22px; padding: 0 5px;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 40% 30%, #c4f0c0, #5fbf63);
  border: 2.5px solid #fff8ea; border-radius: 50%;
  font-weight: 800; font-size: .74rem; color: #1f4a23; box-shadow: 0 2px 5px rgba(45,120,55,.4);
  animation: pts-pulse 1.4s ease-in-out infinite;
}
.portrait-frame .pts-badge.hidden { display: none; }
@keyframes pts-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.12); } }
.char-tab-dot {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px;
  margin-left: 5px; padding: 0 4px; border-radius: 999px; font-size: .72rem; font-weight: 800;
  background: linear-gradient(#c4f0c0, #5fbf63); color: #1f4a23; border: 1.5px solid #fff8ea;
}
/* keep the sub-tabs pinned so you can still switch + see points while scrolling */
.char-tabs { position: sticky; top: 0; z-index: 2; background: var(--paper, #fdf6e6); padding-top: 2px; }
.skill-head {
  position: sticky; top: 48px; z-index: 2; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 6px; padding: 6px 0; background: var(--paper, #fdf6e6);
}
/* compact rows so all four stats fit without hunting for them */
.skill-row { padding: 8px 12px; margin-bottom: 8px; }
.skill-row .r-desc { font-size: .74rem; }
/* MASTERY tab (character panel): the wielded class's rank + its skill list */
.mastery-head { margin-bottom: 10px; }
.mst-title { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 5px; }
.mst-cls { font-weight: 800; color: var(--wood-dark); }
.mst-lv { font-size: .78rem; font-weight: 800; color: var(--gold-hi); }
.mst-bar { height: 14px; }
.mst-tonext { font-size: .72rem; font-weight: 700; color: var(--ink-soft); margin-top: 4px; }
.mst-next { font-size: .76rem; font-weight: 800; color: var(--gold-hi); margin-top: 2px; }
.skill-row.locked { opacity: .55; }
.skill-row .sk-cost { opacity: .7; font-size: .68rem; }
/* effect chips on a skill card — colour-matched to the in-world ailment icons,
   so the player can see "this one bleeds, that one stuns" at a glance */
.skill-fx { display: flex; flex-wrap: wrap; gap: 4px; margin: 3px 0 2px; }
.fx-badge {
  font-size: .6rem; font-weight: 800; line-height: 1; padding: 3px 7px; border-radius: 999px;
  color: var(--fx); background: color-mix(in srgb, var(--fx) 16%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--fx) 45%, transparent);
  text-transform: uppercase; letter-spacing: .3px;
}
.skill-avail { font-size: .95rem; font-weight: 800; color: var(--ink-soft); }
.skill-avail b { font-size: 1.25rem; color: var(--ink); margin-left: 3px; }
.skill-avail.hot b { color: #3f9e47; }
.skill-reset {
  background: #fff8ea; border: 2px solid var(--gold-lo); border-radius: 999px;
  font-weight: 800; padding: 5px 14px; cursor: pointer; color: var(--wood-dark); font-family: var(--font); font-size: .8rem;
}
.skill-reset:active { transform: translateY(1px); }
.skill-row .skill-ic { color: var(--gold-hi); }
.skill-per { color: var(--gold-hi); font-weight: 800; }
.skill-lvl { color: var(--ink-soft); font-weight: 700; }
.recipe .craft-btn.skill-plus { font-size: 1.4rem; line-height: 1; padding: 4px 18px; min-width: 52px; }

/* pressing + feedback: a "+N" pops up right at the button (fixed to the
   viewport, appended to <body> — see UI._popSkillGain) and the row's own
   value badge bounces, so spending a point reads as an obvious event even
   scrolled deep into the stats list, not a subtle text diff to go hunting for. */
.skill-pop {
  position: fixed; transform: translate(-50%, -8px);
  font-weight: 800; font-size: 1rem; color: var(--good);
  text-shadow: 0 1px 2px rgba(94,67,31,.5);
  pointer-events: none; z-index: 9999;
  animation: skill-pop-rise .85s ease-out forwards;
}
@keyframes skill-pop-rise {
  0%   { opacity: 0; transform: translate(-50%, 6px) scale(.8); }
  18%  { opacity: 1; transform: translate(-50%, -8px) scale(1.15); }
  100% { opacity: 0; transform: translate(-50%, -48px) scale(1); }
}
.slot-tag.skill-bump, .stat-chip-val.skill-bump, .cs-stat.skill-bump { animation: skill-bump .45s ease; }
@keyframes skill-bump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.4); color: var(--good); }
  100% { transform: scale(1); }
}
/* gear-change delta pop ("🗡️ +5 · 🛡️ +4") — same rise as .skill-pop but on a
   readable plate (multiple parts to scan) and a touch slower; each part is
   coloured by direction so a swap that trades HP for armor reads honestly. */
.skill-pop.stat-delta {
  display: flex; gap: 8px; white-space: nowrap;
  background: rgba(255,248,234,.96); border: 2px solid var(--gold-lo);
  border-radius: 999px; padding: 4px 12px;
  box-shadow: 0 3px 8px rgba(120,85,45,.25);
  animation-duration: 1.4s;
}
.skill-pop.stat-delta b.up { color: #3f9e47; }
.skill-pop.stat-delta b.down { color: #d9534f; }

/* quests */
.quest {
  background: linear-gradient(180deg, #fffaf0, #faecca); border: 2px solid var(--wood); border-radius: 18px; padding: 12px 14px; margin-bottom: 10px;
  box-shadow: 0 3px 0 rgba(138,95,51,.16); border-left: 6px solid var(--gold);
}
.quest.done { border-left-color: var(--good); opacity: .8; }
.quest.locked { opacity: .55; border-left-color: var(--wood); }
.quest .q-title { font-weight: 800; display: flex; align-items: center; gap: 8px; color: var(--ink); }
.quest .q-desc { font-size: .85rem; color: var(--ink-soft); margin: 3px 0 6px; }
.quest .q-goals { font-size: .82rem; }
.q-goal { display: flex; align-items: center; gap: 7px; padding: 1px 0; color: var(--ink-soft); }
.q-goal .tick { width: 16px; }
.q-goal.met { color: var(--leaf-dark); font-weight: 700; }
.q-reward { font-size: .78rem; margin-top: 6px; color: var(--ink-soft); }
.q-reward b { color: var(--gold-hi); }
.q-claim {
  background: linear-gradient(#ffe19a, var(--gold)); color: #6e4310; border: 2px solid #fff3d4; border-radius: 999px;
  font-weight: 800; padding: 8px 16px; margin-top: 8px; cursor: pointer; box-shadow: 0 3px 0 #c47a22; font-family: var(--font);
}
.q-claim:active { transform: translateY(2px); }

/* ---------------- Dialogue ---------------- */
#dialogue { position: fixed; left: 0; right: 0; bottom: 0; z-index: 45; display: flex; justify-content: center; padding: 16px; pointer-events: none; }
.dialogue-box {
  pointer-events: auto; background: linear-gradient(180deg, #fffaf0, #fbeccb); border-radius: 24px; border: 3px solid var(--wood);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 14px 36px rgba(120,85,45,.32); max-width: 640px; width: 100%; padding: 16px;
  display: flex; gap: 14px; animation: slideUp .25s ease; color: var(--ink);
}
.dialogue-portrait {
  width: 72px; height: 72px; background: radial-gradient(circle at 50% 35%, #fffaf0, #f6e1b9); border-radius: 18px;
  flex-shrink: 0; border: 3px solid var(--wood); image-rendering: pixelated;
}
.dialogue-content { flex: 1; min-width: 0; }
.dialogue-name { font-weight: 800; color: var(--gold-hi); font-size: 1.1rem; }
.dialogue-text { margin: 4px 0 10px; line-height: 1.45; white-space: pre-line; }
.dialogue-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.dialogue-actions .btn { padding: 8px 18px; font-size: .92rem; }
.choice-btn {
  background: linear-gradient(#fffaf0,#f6e1b9); color: var(--wood-dark); border: 2px solid var(--wood); border-radius: 999px; padding: 8px 16px;
  font-weight: 800; cursor: pointer; box-shadow: 0 3px 0 rgba(138,95,51,.25); font-family: var(--font);
}
.choice-btn:active { transform: translateY(2px); }
/* optional sub-label inside a choice (seed ripening time, …) */
.choice-note { display: block; font-weight: 600; font-size: .74rem; opacity: .68; margin-top: 1px; }

/* ---------------- Build banner ---------------- */
#build-banner {
  position: fixed; top: 96px; left: 50%; transform: translateX(-50%); z-index: 30;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9); color: var(--ink); padding: 10px 20px; border-radius: 18px;
  text-align: center; border: 2.5px solid var(--wood); box-shadow: 0 5px 0 rgba(138,95,51,.25);
}
#build-banner-text { font-weight: 800; display: block; color: var(--gold-hi); }
.build-banner-hint { font-size: .8rem; opacity: .85; }

/* Indoors the room is one screen-sized frame, not a scrolling map — a
   center-top banner sits right over the furniture you're trying to look at.
   Tuck it into a small corner badge instead, under the portrait/bars. */
body.in-home #build-banner {
  top: 114px; left: 14px; right: auto; transform: none;
  max-width: 168px; padding: 7px 11px; border-radius: 12px; text-align: left;
}
body.in-home #build-banner-text { font-size: .78rem; }
body.in-home .build-banner-hint { font-size: .64rem; }
body.in-home #rotate-carry-btn, body.in-home #destroy-carry-btn {
  padding: 4px 10px; font-size: .66rem; margin: 6px 4px 0 0;
}
body.in-home #rotate-carry-btn .rc-icon, body.in-home #destroy-carry-btn .dc-icon { width: .8rem; height: .8rem; }

/* banner action row — "Flip" (blue) + "Destroy" (red) pills. inline-flex so
   they sit side by side, centered under the banner text via its text-align. */
#rotate-carry-btn, #destroy-carry-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  margin: 8px 4px 0; padding: 6px 16px;
  color: #fff; font-weight: 800; font-size: .8rem;
  border-radius: 999px; cursor: pointer; font-family: var(--font); transition: transform .1s ease, filter .15s;
}
#rotate-carry-btn:active, #destroy-carry-btn:active { transform: translateY(2px); }
#rotate-carry-btn .rc-icon, #destroy-carry-btn .dc-icon { width: 1rem; height: 1rem; }

/* "Flip" — only appears while carrying/placing furniture (Building.canRotate) */
#rotate-carry-btn {
  background: linear-gradient(180deg, #bfe3ff, #6fb6ee); border: 2px solid #3f8fc4;
  box-shadow: 0 3px 0 rgba(30,80,120,.35);
}
#rotate-carry-btn:hover { filter: brightness(1.06); }

/* "Destroy" — only appears while carrying home furniture (Building.canDestroyCarried);
   a deliberate second color (red) so it never gets mistaken for Flip or the
   ordinary "tap to cancel" affordance of the banner. */
#destroy-carry-btn {
  background: linear-gradient(180deg, #ff8a7a, #e0453a); border: 2px solid #b32c22;
  box-shadow: 0 3px 0 rgba(140,30,20,.35);
}
#destroy-carry-btn:hover { filter: brightness(1.06); }

/* ---------------- Touch controls ---------------- */
#joystick {
  position: absolute; bottom: calc(110px + env(safe-area-inset-bottom)); left: calc(24px + env(safe-area-inset-left));
  width: 120px; height: 120px;
  background: rgba(255,248,234,.45); border-radius: 50%; box-shadow: inset 0 0 0 3px rgba(210,168,110,.6);
  touch-action: none;
}
#joystick-knob {
  position: absolute; top: 50%; left: 50%; width: 52px; height: 52px; margin: -26px 0 0 -26px;
  background: radial-gradient(circle at 40% 35%, #fffaf0, #f6e1b9); border: 3px solid var(--wood); border-radius: 50%; box-shadow: 0 4px 0 rgba(138,95,51,.28);
}

/* ---------------- Hotbar (slot group inside the dock) ---------------- */
/* ---------------- skill bar (weapon skills — R/T/Z/X) ----------------
   Desktop: a row of four slots at the head of the dock, left of the hotbar.
   Touch: the same element floats up beside the action button as a 2×2 pad.
   `--sk-col` is set per slot from the skill's own colour (GameData.SKILLS). */
#skillbar { display: flex; gap: 6px; flex-wrap: nowrap; }
#skillbar.empty { display: none; }   /* wielding a class with no learned skills */
#skillbar.empty + .sk-divider { display: none; }   /* no skill bar → no divider to show */
.sk-slot {
  position: relative; overflow: hidden; padding: 0;
  display: flex; align-items: center; justify-content: center;
  width: 48px; height: 54px; cursor: pointer;
  background: linear-gradient(180deg, #2a2016, #40301d);
  border: 2.5px solid var(--wood); border-radius: 14px;
  box-shadow: inset 0 1px 0 rgba(255,248,234,.18), 0 2px 0 rgba(138,95,51,.2);
  transition: transform .1s ease, filter .12s;
}
.sk-slot.empty { display: none; }
.sk-slot:hover { filter: brightness(1.12); transform: translateY(-2px); }
.sk-slot:active { transform: translateY(0) scale(.96); }
.sk-slot .sk-icon { width: 26px; height: 26px; color: var(--sk-col, #ffe27a); filter: drop-shadow(0 0 5px var(--sk-col, #ffe27a)); z-index: 1; }
.sk-slot .sk-key { position: absolute; top: 2px; left: 5px; font-size: .56rem; font-weight: 800; color: rgba(255,248,234,.75); line-height: 1; z-index: 2; }
/* cooldown: a dark curtain that drains downward, with the seconds left on top */
.sk-slot .sk-cd { position: absolute; left: 0; right: 0; top: 0; height: 0%; background: rgba(10,6,3,.72); z-index: 1; pointer-events: none; }
.sk-slot .sk-cdn { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 2; font-size: .95rem; font-weight: 800; color: #fff8ea; text-shadow: 0 1px 3px #000; pointer-events: none; }
.sk-slot.cooling .sk-icon { opacity: .35; filter: none; }
.sk-slot.poor { filter: grayscale(.7) brightness(.8); }   /* not enough stamina to cast */
/* onboarding — the bar pulses from the first sword wielded until the first cast */
#skillbar.sk-teach .sk-slot:not(.empty) { border-color: #6fd3ff; animation: skTeachPulse 1.1s ease-in-out infinite; }
@keyframes skTeachPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(111,211,255,.7); }
  50%      { box-shadow: 0 0 0 7px rgba(111,211,255,0), 0 0 14px rgba(111,211,255,.9); }
}
.sk-slot.poor .sk-icon { opacity: .5; }
/* touch: the always-on pad read as clutter with only one thing to swing at —
   hold the action button instead (skill wheel, below). Bar + its divider stay
   in the DOM (desktop still uses them) but never show on a touch device. */
body.touch-mode #skillbar,
body.touch-mode #skillbar + .sk-divider {
  display: none;
}

/* the action button pulses the same way #skillbar used to while the skill
   feature is unexplained (see UI._teachSkills) — it's the thing to hold now */
#action-btn.sk-teach { animation: abTeachPulse 1.1s ease-in-out infinite; }
@keyframes abTeachPulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--wood), 0 6px 14px rgba(120,85,45,.3); }
  50%      { box-shadow: 0 0 0 3px var(--wood), 0 6px 14px rgba(120,85,45,.3), 0 0 0 9px rgba(111,211,255,0), 0 0 20px rgba(111,211,255,.9); }
}

/* skill wheel (touch) — hold the action button to open. Same glass-circle ring
   language and drag-out-or-tap gesture as #emote-wheel, fanned into the
   up-left quarter around the button instead of a full ring around the player. */
#skill-wheel { position: fixed; inset: 0; z-index: 115; -webkit-tap-highlight-color: transparent; }
#skill-wheel.hidden { display: none; }
.sw-ring { position: absolute; width: 0; height: 0; }
.sw-item {
  position: absolute; left: 50%; top: 50%;
  width: 54px; height: 54px; padding: 0; cursor: pointer; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: rgba(31,22,12,.42);
  border: 1.5px solid rgba(255,239,210,.3); border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06), 0 3px 10px rgba(0,0,0,.28);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  animation: ew-pop .16s ease-out both;
  transition: background .1s, border-color .1s, box-shadow .1s;
}
.sw-item .sw-ic { width: 1.5rem; height: 1.5rem; color: var(--sk, #ffe27a); filter: drop-shadow(0 0 4px var(--sk, #ffe27a)); pointer-events: none; position: relative; z-index: 1; }
.sw-item .sw-lb {
  font-size: .5rem; font-weight: 800; letter-spacing: .2px; line-height: 1;
  color: rgba(255,247,232,.78); pointer-events: none; max-width: 50px; position: relative; z-index: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sw-item.on, .sw-item:hover {
  background: rgba(76,54,28,.74);
  border-color: var(--sk, #ffe7bd);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.1), 0 0 16px -2px var(--sk, #ffe7bd);
}
.sw-item.on .sw-lb { color: rgba(255,252,244,.96); }
.sw-item .sw-cd { position: absolute; left: 0; right: 0; top: 0; height: 0%; background: rgba(10,6,3,.72); z-index: 0; pointer-events: none; }
.sw-item .sw-cdn { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; z-index: 1; font-size: .85rem; font-weight: 800; color: #fff8ea; text-shadow: 0 1px 3px #000; pointer-events: none; }
.sw-item.cooling .sw-ic { opacity: .35; filter: none; }
.sw-item.poor { filter: grayscale(.7) brightness(.8); }

#hotbar { display: flex; gap: 6px; flex-wrap: nowrap; }
.hb-slot {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: linear-gradient(180deg, #fffaf0, #f1ddb4); border: 2.5px solid var(--wood); border-radius: 14px; cursor: pointer;
  width: 48px; height: 54px; position: relative;
  box-shadow: inset 0 1px 0 #fff8ea, 0 2px 0 rgba(138,95,51,.2); transition: transform .1s ease, filter .12s;
}
.hb-slot:hover { filter: brightness(1.04); transform: translateY(-2px); }
.hb-slot:active { transform: translateY(0); }
.hb-slot.empty { opacity: .9; background: linear-gradient(180deg, #f3e6cb, #e7d4ac); }
.hb-slot.empty .hb-plus {
  font-size: 1.15rem; font-weight: 800; line-height: 1;
  color: var(--wood); opacity: .38;
}
.hb-slot.empty:hover .hb-plus { opacity: .7; }
.hb-slot.depleted { opacity: .5; filter: grayscale(.45); }
.hb-slot.active { border-color: var(--gold); box-shadow: 0 0 0 2px var(--gold), inset 0 1px 0 #fff8ea, 0 2px 8px rgba(245,167,63,.4); transform: translateY(-2px); }
/* onboarding: pulse the slot the coconut just landed in AND float an arrow above
   it ("tap here"), inviting a tap-to-drink. Both clear the moment the player uses
   that slot (see Game.useHotbarSlot — the .hb-teach class is dropped). */
.hb-slot.hb-teach { border-color: #6fd3ff; z-index: 4; overflow: visible; animation: hbTeachPulse 1.1s ease-in-out infinite; }
@keyframes hbTeachPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(111,211,255,.7), inset 0 1px 0 #fff8ea; transform: translateY(0); }
  50%      { box-shadow: 0 0 0 6px rgba(111,211,255,0), 0 0 14px rgba(111,211,255,.9), inset 0 1px 0 #fff8ea; transform: translateY(-3px); }
}
/* bouncing downward arrow hovering over the slot — "click here" */
.hb-slot.hb-teach::after {
  content: ""; position: absolute; left: 50%; top: -16px;
  width: 0; height: 0; margin-left: -9px;
  border-left: 9px solid transparent; border-right: 9px solid transparent;
  border-top: 13px solid #6fd3ff;
  filter: drop-shadow(0 2px 2px rgba(0,0,0,.4)); pointer-events: none;
  animation: hbTeachArrow .8s ease-in-out infinite;
}
@keyframes hbTeachArrow {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
.hb-slot .hb-key { position: absolute; top: 2px; left: 5px; font-size: .56rem; font-weight: 800; color: var(--wood-dark); opacity: .8; line-height: 1; }
.hb-slot .hb-icon { font-size: 1.3rem; line-height: 1; }
.hb-slot .hb-qty { position: absolute; bottom: 2px; right: 5px; font-size: .6rem; font-weight: 800; color: var(--gold-hi); }

/* โหมดถืออาวุธ — the combat-mode toggle at the head of the hotbar. Off = muted
   sword; On = lit gold sword (a wielded WEAPON keeps its place in hand and never
   gathers; a wielded tool still works — see Game._handleInteraction). */
#weap-lock {
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 54px; flex: 0 0 auto; margin-right: 4px;
  background: linear-gradient(180deg, #fffaf0, #f1ddb4);
  border: 2.5px solid var(--wood); border-radius: 14px; cursor: pointer;
  box-shadow: inset 0 1px 0 #fff8ea, 0 2px 0 rgba(138,95,51,.2);
  color: var(--wood); transition: transform .1s ease, filter .12s, color .12s;
}
#weap-lock:hover { filter: brightness(1.04); transform: translateY(-2px); }
#weap-lock:active { transform: translateY(0); }
#weap-lock .wl-ic { width: 20px; height: 20px; opacity: .5; }
#weap-lock.on {
  border-color: var(--gold); color: var(--gold-hi);
  box-shadow: 0 0 0 2px var(--gold), inset 0 1px 0 #fff8ea, 0 2px 8px rgba(245,167,63,.4);
}
#weap-lock.on .wl-ic { opacity: 1; }

/* Hotbar item picker popup */
#hotbar-picker {
  position: fixed; z-index: 50; max-width: 248px; width: max-content;
  max-height: 78vh; display: flex; flex-direction: column;
  background: linear-gradient(180deg, #fffaf0, #fbeccb); border-radius: 18px; border: 3px solid var(--wood);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 10px 26px rgba(120,85,45,.32); padding: 10px 12px;
  animation: pop .15s cubic-bezier(.2,1.2,.4,1);
}
.hp-title { font-weight: 800; color: var(--gold-hi); font-size: .85rem; margin-bottom: 8px; flex: 0 0 auto; }
/* scroll the grid (not the whole popup) so a big bag never pushes items or the
   clear button off-screen */
.hp-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; overflow-y: auto; flex: 1 1 auto; min-height: 0; padding-right: 2px; }
.hp-cell {
  display: flex; flex-direction: column; align-items: center;
  background: linear-gradient(180deg, #fffaf0, #faecca); border: 2px solid var(--wood); border-radius: 12px; padding: 5px 3px;
  cursor: pointer; text-align: center; box-shadow: 0 2px 0 rgba(138,95,51,.18); transition: filter .1s, transform .08s;
}
.hp-cell.hp-on { border-color: var(--gold); box-shadow: 0 0 0 2px var(--gold), 0 2px 0 rgba(138,95,51,.18); }
.hp-cell:hover { filter: brightness(1.04); transform: translateY(-1px); }
.hp-cell .hp-icon { font-size: 1.3rem; line-height: 1; }
.hp-cell .hp-qty { font-size: .6rem; font-weight: 800; color: var(--ink-soft); }
.hp-empty-note { opacity: .7; font-style: italic; font-size: .78rem; text-align: center; padding: 6px 0; color: var(--ink-soft); }
.hp-clear {
  width: 100%; margin-top: 8px; padding: 6px 10px; flex: 0 0 auto;
  background: linear-gradient(#ff9a8d,#f06d6d); color: #fff; border: 2px solid #ffd3cc;
  border-radius: 999px; font-weight: 800; cursor: pointer; font-family: var(--font);
  box-shadow: 0 3px 0 #c44b41; font-size: .8rem;
}
.hp-clear:active { transform: translateY(2px); }

/* ---------------- backpack: compact two-row header ---------------- */
/* row 1 — replaces the shared .panel-header (hidden for this panel): the
   panel title, the coin pill and the close button share one line */
.inv-head {
  flex: 0 0 auto; display: flex; align-items: center; gap: 12px;
  padding: 10px 18px; border-bottom: 2.5px dashed var(--wood);
}
.inv-head-title { flex: 1 1 auto; margin: 0; color: var(--gold-hi); font-size: 1.15rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.inv-head .inv-coin-row { margin: 0; flex: 0 0 auto; }
.inv-x { flex: 0 0 auto; }
/* row 2 — search + capacity meter + upgrade button on one line; the verbose
   upgrade label is dropped here (the button says it all) */
.inv-hrow { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin: 0 0 8px; }
.inv-hrow .inv-search-wrap { margin: 0; flex: 2 1 170px; min-width: 150px; }
.inv-hrow .inv-cap { margin: 0; flex: 1 1 150px; min-width: 140px; }
.inv-hrow .bag-up { margin: 0; flex: 0 0 auto; flex-wrap: nowrap; }
.inv-hrow .bag-up-label { display: none; }
.inv-hrow .bag-up.maxed { display: none; }
.inv-cap { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
.inv-cap-bar { flex: 1 1 auto; height: 9px; border-radius: 999px; background: rgba(138,95,51,.16); overflow: hidden; box-shadow: inset 0 1px 2px rgba(120,85,45,.2); }
.inv-cap-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, #8fd07a, var(--gold)); transition: width .25s ease; }
.inv-cap-label { flex: 0 0 auto; font-size: .72rem; font-weight: 800; color: var(--ink-soft); white-space: nowrap; }
.inv-cap.full .inv-cap-fill { background: linear-gradient(90deg, #f0a24a, #e0463b); }
.inv-cap.full .inv-cap-label { color: #d2463b; }

/* backpack upgrade row — sits under the capacity meter */
.bag-up { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; margin: 0 0 10px; }
.bag-up-label { flex: 1 1 auto; font-size: .72rem; font-weight: 800; color: var(--ink-soft); white-space: nowrap; }
.bag-up-costs { display: flex; flex-wrap: wrap; gap: 4px; }
.bag-up-cost { font-size: .68rem; font-weight: 800; color: #d2463b; padding: 1px 6px; border-radius: 999px; background: rgba(224,70,59,.1); white-space: nowrap; }
.bag-up-cost.ok { color: #4a9c3e; background: rgba(74,156,62,.12); }
.bag-up-btn { flex: 0 0 auto; font-size: .72rem; font-weight: 800; color: #fff; padding: 4px 12px; border: none; border-radius: 999px; background: linear-gradient(90deg, #8fd07a, var(--gold)); cursor: pointer; box-shadow: 0 1px 3px rgba(120,85,45,.3); }
.bag-up-btn:disabled { opacity: .45; filter: grayscale(.5); cursor: default; box-shadow: none; }
.bag-up.maxed .bag-up-label { color: var(--gold); text-align: center; flex-basis: 100%; }

.inv-coin-row {
  display: inline-flex; align-items: center; gap: 6px;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 2px solid var(--wood); border-radius: 999px; padding: 3px 14px 3px 6px;
  box-shadow: inset 0 0 0 1.5px #fff8ea, 0 2px 5px rgba(120,85,45,.15);
  margin: 0 0 10px;
}
.inv-coin-row .coin-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.25rem; height: 1.25rem; border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, #ffe89a, #e0a52e);
  color: #7a5410; font-weight: 900; font-size: .88rem;
  box-shadow: inset 0 0 0 1.5px #fff3c8, 0 1px 2px rgba(120,85,45,.4);
}
.inv-coin-val { font-weight: 800; color: var(--gold-hi); font-size: 1rem; }
.inv-coin-label { font-size: .72rem; font-weight: 700; color: var(--ink-soft); }

.inv-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.inv-chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px;
  background: linear-gradient(#fffaf0, #f4e3bf); color: var(--wood-dark);
  border: 2px solid var(--wood); border-radius: 999px; font-family: var(--font);
  font-weight: 800; font-size: .74rem; cursor: pointer; box-shadow: 0 2px 0 rgba(138,95,51,.18);
  transition: filter .1s, transform .08s;
}
.inv-chip:hover { filter: brightness(1.03); }
.inv-chip.on { background: linear-gradient(#ffe19a, var(--gold)); color: var(--wood-dark); box-shadow: 0 0 0 2px var(--gold-hi), 0 2px 0 rgba(138,95,51,.18); }
.inv-chip .chip-n { font-size: .62rem; opacity: .8; background: rgba(255,255,255,.45); border-radius: 999px; padding: 0 5px; }

/* category header rows inside the backpack grid ("all" view) — full-width
   dividers so the bag reads as organised sections, not one big pile */
.inv-group-h {
  grid-column: 1 / -1; margin: 2px 0 -4px;
  font-size: .68rem; font-weight: 800; color: var(--gold-hi);
  letter-spacing: .7px; text-transform: uppercase;
  display: flex; align-items: center; gap: 8px;
}
.inv-group-h::before, .inv-group-h::after { content: ""; flex: 1; height: 1.5px; background: var(--gold-lo); }

/* backpack cells are now tappable; ring the ones already on the hotbar */
.cell[data-item] { cursor: pointer; transition: filter .1s, transform .08s; }
.cell[data-item]:hover { filter: brightness(1.04); transform: translateY(-1px); }
.cell.on-hotbar { border-color: var(--gold); box-shadow: 0 0 0 2px var(--gold), 0 3px 0 rgba(138,95,51,.18); }
.cell.on-hotbar::after { content: "⭐"; position: absolute; top: 2px; left: 5px; font-size: .68rem; }
/* the currently-selected item (its detail is shown in the sticky footer) */
.cell.sel { border-color: var(--gold-hi); box-shadow: 0 0 0 3px var(--gold-hi), 0 3px 0 rgba(138,95,51,.18); transform: translateY(-1px); }
/* qty now sits in a pill top-right (like the mockup) instead of a bare number */
.cell .qty {
  position: absolute; top: 4px; right: 4px; bottom: auto;
  min-width: 18px; padding: 1px 5px; border-radius: 999px;
  background: rgba(94,67,31,.82); color: #fff8ea;
  font-weight: 800; font-size: .68rem; line-height: 1.3; text-align: center;
}

/* backpack layout: fixed-height frame → pinned header + scrolling grid + pinned
   detail footer, so switching category tabs never resizes the panel */
.panel.panel-inv { height: min(620px, 86vh); }
#panel-body.pb-inv { display: flex; flex-direction: column; overflow: hidden; padding: 0; }
.inv-top { flex: 0 0 auto; padding: 10px 18px 6px; }
.inv-scroll { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; min-height: 0; padding: 8px 18px 12px; overscroll-behavior: contain; }

/* backpack search box */
.inv-search-wrap { position: relative; margin: 0 0 10px; }
.inv-search-ic { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-size: .9rem; opacity: .55; pointer-events: none; }
.inv-search {
  width: 100%; box-sizing: border-box; padding: 9px 12px 9px 34px;
  border: 2px solid var(--wood); border-radius: 999px; background: #fffaf0;
  font-family: var(--font); font-size: .82rem; font-weight: 700; color: var(--ink);
  box-shadow: inset 0 1px 2px rgba(120,85,45,.14);
}
.inv-search::placeholder { color: var(--ink-soft); opacity: .7; font-weight: 700; }
.inv-search:focus { outline: none; border-color: var(--gold-hi); }

/* category tabs reuse the crafting page's .char-tab pills; keep them at their
   natural width (the craft-tabs flex-grow stretches 2-3 tabs into giant slabs
   early game when the bag only holds a couple of categories) */
.inv-tabs { margin-bottom: 0; }
.inv-tabs .char-tab { flex: 0 1 auto; padding: 6px 12px; font-size: .78rem; }
.inv-tabs .chip-n {
  margin-left: 5px; font-size: .62rem; opacity: .85;
  background: rgba(255,255,255,.5); border-radius: 999px; padding: 0 5px;
}
.inv-tabs .char-tab.on .chip-n { background: rgba(255,255,255,.6); }

/* item-detail footer — pinned under the grid, kept to two slim lines
   (icon + name/qty/desc, then one button row) so it never swallows the grid */
.inv-detail {
  flex: 0 0 auto; margin: 6px 14px 12px; z-index: 3;
  display: flex; flex-direction: column; gap: 8px; padding: 8px 12px;
  border: 2px solid var(--wood); border-radius: 14px;
  background: linear-gradient(180deg, #fffaf0, #f6e6c4);
  box-shadow: 0 4px 14px rgba(120,85,45,.28);
}
.inv-detail.empty { align-items: center; justify-content: center; min-height: 34px; color: var(--ink-soft); }
.invd-hint { font-size: .74rem; font-style: italic; opacity: .8; }
.invd-main { display: flex; align-items: center; gap: 10px; }
.invd-ic { flex: 0 0 auto; font-size: 1.7rem; line-height: 1; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; background: #fff8ea; border: 2px solid var(--gold-lo); border-radius: 10px; }
.invd-body { flex: 1 1 auto; min-width: 0; }
.invd-top { display: flex; align-items: baseline; gap: 8px; }
.invd-name { font-weight: 800; color: var(--ink); font-size: .88rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.invd-sub { flex: 0 0 auto; font-size: .68rem; font-weight: 800; color: var(--gold-hi); white-space: nowrap; }
.invd-desc { font-size: .72rem; color: var(--ink-soft); font-weight: 600; margin: 1px 0 0; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* "หาได้จาก" on the backpack card — one line, clipped rather than wrapped so
   the detail panel's height never jumps as you tap through the grid. */
.inv-detail .r-gs { font-size: .72rem; margin: 3px 0 0; }
.inv-detail .r-up { margin-top: 4px; padding: 4px 7px; }
.invd-src { font-size: .7rem; font-weight: 700; color: var(--ink-soft); margin: 3px 0 0; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.invd-src-h { font-weight: 800; color: var(--gold-hi); }
.inv-detail .r-nutri { font-size: .76rem; font-weight: 800; color: var(--leaf-dark); margin: 3px 0 0; white-space: nowrap; }
.inv-detail .r-nutri .neg { color: var(--bad); }
.invd-actions { flex: 0 0 auto; display: flex; flex-direction: row; gap: 8px; }
.invd-btn {
  flex: 1 1 0; text-align: center;
  padding: 8px 10px; border: 2px solid var(--wood); border-radius: 12px; font-family: var(--font);
  font-weight: 800; font-size: .8rem; cursor: pointer; white-space: nowrap; color: #fff8ea;
  box-shadow: 0 2px 0 rgba(138,95,51,.22); transition: filter .1s, transform .08s;
}
.invd-btn:hover { filter: brightness(1.05); }
.invd-btn:active { transform: translateY(2px); box-shadow: none; }
.invd-btn:disabled { opacity: .4; cursor: default; filter: grayscale(.4); box-shadow: none; }
.invd-use { background: linear-gradient(#8fcf66, #5a9e5e); border-color: #5a9e5e; }
.invd-craft { background: linear-gradient(var(--gold), var(--gold-hi)); border-color: var(--gold-hi); }
.invd-drop { background: linear-gradient(#f4977b, #e0463b); border-color: #e0463b; }

/* very short landscape screens: drop the flavour line so the item grid always
   keeps at least a full row visible above the detail footer */
@media (max-height: 460px) {
  .invd-desc { display: none; }
  .inv-detail { gap: 6px; padding: 6px 10px; }
}

/* crafting: "recipes using <item>" filter bar */
.craft-ingbar { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; padding: 8px 12px; background: rgba(196,122,34,.12); border: 2px solid var(--gold-lo); border-radius: 12px; }
.craft-ingbar-txt { flex: 1 1 auto; font-weight: 800; font-size: .8rem; color: var(--ink); }
.craft-ingclear { flex: 0 0 auto; padding: 4px 12px; border: 2px solid var(--wood); border-radius: 999px; background: linear-gradient(#fffaf0, #f4e3bf); color: var(--wood-dark); font-family: var(--font); font-weight: 800; font-size: .72rem; cursor: pointer; }
.craft-ingclear:active { transform: translateY(1px); }

/* ---- NPC trade screen (Buy / Sell) ---- */
/* LPC-sprite item icons: the real art over an emoji fallback that hides once it
   loads. Weapons/shields are a pre-baked 128px PNG (tools/gen_item_icons.py) in
   an <img>, so the browser scales them for the display — the old 48x48 canvas
   was upscaled by CSS and came out half-resolution on retina Macs & iPhones.
   object-fit keeps the aspect ratio, since these wraps are not all square. */
/* inline item icon (recipe cost lines, quest chips, reward pips) — sized in em
   so it matches whatever text it sits in, and drops onto the text baseline */
.ic-sm { display: inline-block; width: 1.15em; height: 1.15em; vertical-align: -0.22em;
         object-fit: contain; image-rendering: pixelated; }
.weap-wrap { position: relative; display: inline-flex; width: 42px; height: 42px; align-items: center; justify-content: center; }
.weap-wrap .weap-fb { font-size: 1.8rem; line-height: 1; }
.weap-wrap .weap-img, .weap-wrap .shop-item-cv { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; image-rendering: pixelated; display: none; }
.weap-wrap.cv-ok .weap-img, .weap-wrap.cv-ok .shop-item-cv { display: block; }
.weap-wrap.cv-ok .weap-fb { visibility: hidden; }
.shop-bar-item .weap-wrap { width: 34px; height: 34px; flex: 0 0 auto; }
.r-icon .weap-wrap { width: 100%; height: 44px; }
/* structures are wide (a 2x2 hut, a 2x1 farm bed) — the weapon-sized box
   letterboxed them down to nothing, so build rows get a bigger square */
.recipe .r-icon-bld { width: 3.5rem; }
.r-icon-bld .weap-wrap { height: 56px; }
.hb-slot .weap-wrap { width: 24px; height: 24px; }
.hp-cell .weap-wrap { width: 26px; height: 26px; }
.ia-head .weap-wrap { width: 22px; height: 22px; }
.shop-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.shop-head .inv-coin-row { margin: 0; flex: 0 0 auto; }
.shop-greet { font-size: .85rem; font-weight: 700; color: var(--ink-soft); font-style: italic; }
.shop-tabs { margin-bottom: 12px; }
.shop-grid { padding-bottom: 86px; grid-template-columns: repeat(auto-fill, minmax(108px, 1fr)); }  /* room so the sticky bar never covers the last row */
.shop-cell { padding: 12px 6px 8px; }
.shop-cell .weap-wrap { width: 72px; height: 78px; }
.shop-cell .emoji { font-size: 2.8rem; }
.shop-cell .cname { font-size: .74rem; margin-top: 5px; }
.shop-cell { cursor: pointer; transition: filter .1s, transform .08s; }
.shop-cell:hover { filter: brightness(1.04); transform: translateY(-1px); }
.shop-cell.sel { border-color: var(--gold); box-shadow: 0 0 0 2.5px var(--gold), 0 3px 0 rgba(138,95,51,.18); }
.shop-cell.broke { opacity: .55; }
/* a locked shelf row: still legible (the point is that you SEE what you can't
   buy yet), desaturated rather than dimmed to nothing, with its condition in
   place of the buy affordance */
.shop-cell.locked { opacity: .72; filter: saturate(.35); cursor: help; }
.shop-cell.locked .shop-price { color: var(--ink-soft); text-decoration: line-through; }
.shop-lock { margin-top: 4px; font-size: .62rem; line-height: 1.25; font-weight: 700; color: var(--ink-soft); }
.shop-stat { margin-top: 4px; font-size: .64rem; line-height: 1.3; color: var(--ink-soft); font-weight: 700; }
.shop-price { margin-top: 3px; font-size: .78rem; font-weight: 800; color: var(--gold-hi); }
.shop-price .coin-ico { font-size: .72rem; margin-left: 1px; }
.shop-bar {
  position: sticky; bottom: 0; z-index: 3; margin-top: 12px; padding: 10px 12px;
  background: linear-gradient(#fffaf0, #f4e3bf); border: 2.5px solid var(--wood);
  border-radius: 16px; box-shadow: 0 -3px 12px rgba(90,60,30,.18);
}
.shop-bar.hint { text-align: center; font-weight: 700; color: var(--ink-soft); font-size: .85rem; }
.shop-bar-item { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.shop-bar-item .emoji { font-size: 1.4rem; line-height: 1; }
.shop-bar-name { font-weight: 800; color: var(--ink); font-size: .92rem; }
.shop-bar-unit { margin-left: auto; font-size: .8rem; font-weight: 700; color: var(--ink-soft); }
.shop-bar-ctrl { display: flex; align-items: center; gap: 8px; }
.shop-step {
  min-width: 40px; height: 38px; border-radius: 12px; font-family: var(--font); font-weight: 800; font-size: 1.05rem;
  cursor: pointer; color: var(--wood-dark); background: linear-gradient(#fffaf0, #f4e3bf);
  border: 2.5px solid var(--wood); box-shadow: 0 3px 0 rgba(138,95,51,.18);
}
.shop-step:active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(138,95,51,.18); }
.shop-max { font-size: .8rem; padding: 0 10px; }
.shop-qty { min-width: 34px; text-align: center; font-weight: 800; font-size: 1.1rem; color: var(--ink); }
.shop-confirm {
  margin-left: auto; padding: 0 18px; height: 42px; border-radius: 14px; font-family: var(--font);
  font-weight: 800; font-size: .95rem; cursor: pointer; color: var(--wood-dark);
  background: linear-gradient(#ffe19a, var(--gold)); border: 2.5px solid var(--gold-hi);
  box-shadow: 0 3px 0 rgba(138,95,51,.25);
}
.shop-confirm.sellmode { background: linear-gradient(#c4f0c0, #5fbf63); color: #1f4a23; border-color: #8fd894; }
.shop-confirm.off { filter: grayscale(.6) brightness(.9); opacity: .7; }
.shop-confirm:active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(138,95,51,.25); }

/* ---------------- backpack item-action popup ---------------- */
#item-actions {
  position: fixed; z-index: 60; width: 184px; padding: 8px;
  background: linear-gradient(180deg, #fffaf0, #fbeccb); border-radius: 16px; border: 3px solid var(--wood);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 10px 26px rgba(120,85,45,.32);
  animation: pop .14s cubic-bezier(.2,1.2,.4,1);
}
.ia-head { display: flex; align-items: center; gap: 6px; padding: 2px 4px 8px; border-bottom: 2px dashed var(--gold-lo); margin-bottom: 6px; }
.ia-head .ia-ic { font-size: 1.2rem; }
.ia-head .ia-name { font-weight: 800; color: var(--ink); font-size: .82rem; flex: 1 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ia-head .ia-q { font-weight: 800; color: var(--gold-hi); font-size: .74rem; }
.ia-btn {
  display: block; width: 100%; margin-top: 5px; padding: 7px 10px; text-align: left;
  background: linear-gradient(#fffaf0, #f4e3bf); color: var(--wood-dark);
  border: 2px solid var(--wood); border-radius: 10px; font-family: var(--font);
  font-weight: 800; font-size: .8rem; cursor: pointer; box-shadow: 0 2px 0 rgba(138,95,51,.18);
}
.ia-btn:hover { filter: brightness(1.04); }
.ia-btn:active { transform: translateY(2px); box-shadow: none; }
.ia-btn.ia-eat, .ia-btn.ia-drink { background: linear-gradient(#bfe89a, #8fcf66); color: #2f5320; border-color: #8fbf5a; }
.ia-btn.ia-hotbar { background: linear-gradient(#ffe19a, var(--gold)); border-color: var(--gold-hi); }
.ia-btn.ia-drop, .ia-btn.ia-dropAll { background: linear-gradient(#ffd0c8, #f4a99e); color: #8a3328; border-color: #e89488; }

/* ---------------- storage chest transfer modal ---------------- */
#storage-modal {
  position: fixed; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center;
  background: rgba(60,40,20,.5); backdrop-filter: blur(2px); padding: 16px;
  animation: pop .14s ease;
}
.st-card {
  width: min(620px, 96vw); max-height: 88vh; display: flex; flex-direction: column;
  background: linear-gradient(180deg, #fff8ea, #f4e3bf); border: 4px solid var(--wood); border-radius: 22px;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 18px 44px rgba(80,55,25,.45); padding: 14px 16px;
}
.st-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.st-title { font-weight: 800; color: var(--gold-hi); font-size: 1.15rem; }
.st-close {
  width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--wood); cursor: pointer;
  background: linear-gradient(#fffaf0, #f6e1b9); color: var(--wood-dark); font-weight: 800; font-size: 1rem; line-height: 1;
}
.st-tools { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.st-qlabel { font-size: .72rem; color: var(--ink-soft); font-style: italic; }
.st-qrow { display: inline-flex; gap: 5px; }
.st-qbtn {
  padding: 5px 12px; border: 2px solid var(--wood); border-radius: 999px; cursor: pointer; font-family: var(--font);
  font-weight: 800; font-size: .78rem; color: var(--wood-dark); background: linear-gradient(#fffaf0, #f4e3bf); box-shadow: 0 2px 0 rgba(138,95,51,.18);
}
.st-qbtn.on { background: linear-gradient(#ffe19a, var(--gold)); box-shadow: 0 0 0 2px var(--gold-hi), 0 2px 0 rgba(138,95,51,.18); }
.st-stash {
  margin-top: 8px; flex: 0 0 auto; padding: 7px 14px; border: 2px solid var(--wood-dark); border-radius: 999px; cursor: pointer; font-family: var(--font);
  font-weight: 800; font-size: .78rem; color: #fff; background: linear-gradient(#7fb98a, #4d8f63); box-shadow: 0 3px 0 #3a6e4b;
}
.st-stash:active { transform: translateY(2px); box-shadow: none; }
.st-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: stretch; min-height: 0; flex: 1 1 auto; }
.st-col { display: flex; flex-direction: column; min-height: 0; }
.st-colhead { font-weight: 800; color: var(--ink); font-size: .82rem; margin-bottom: 6px; display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.st-cap { font-size: .66rem; font-weight: 800; color: var(--gold-hi); background: rgba(255,255,255,.5); border-radius: 999px; padding: 0 6px; }
.st-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); gap: 7px; align-content: start;
  overflow-y: auto; flex: 1 1 auto; min-height: 90px;
  background: rgba(138,95,51,.07); border: 2px solid var(--gold-lo); border-radius: 14px; padding: 8px;
}
.st-cell {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  aspect-ratio: 1 / 1; background: linear-gradient(180deg, #fffaf0, #faecca); border: 2px solid var(--wood);
  border-radius: 12px; cursor: pointer; box-shadow: 0 2px 0 rgba(138,95,51,.18); transition: filter .1s, transform .08s;
}
.st-cell:hover { filter: brightness(1.05); transform: translateY(-1px); }
.st-cell:active { transform: translateY(1px); }
/* holds the SAME baked item PNG the backpack cell paints (UI._weaponIcon), with
   the emoji as its fallback — the transfer screen shows both sides at once, so
   a plank wearing two faces on it is the most visible place that rule can break */
.st-cell .st-ic { font-size: 1.4rem; line-height: 1; display: flex; align-items: center; justify-content: center; }
.st-cell .weap-wrap { width: 30px; height: 30px; }
.st-cell .weap-wrap .weap-fb { font-size: 1.4rem; }
.st-cell .st-q { position: absolute; bottom: 1px; right: 4px; font-size: .6rem; font-weight: 800; color: var(--gold-hi); }
.st-empty { grid-column: 1 / -1; text-align: center; color: var(--ink-soft); opacity: .8; font-style: italic; font-size: .78rem; padding: 18px 6px; }
.st-cell-drag-src { opacity: .3; }
.st-drag-ghost {
  position: fixed; top: 0; left: 0; z-index: 999; pointer-events: none;
  transform: translate(-50%, -50%) scale(1.1); opacity: .92; border-radius: 12px;
  box-shadow: 0 10px 24px rgba(40,25,10,.4);
}
.st-grid-drop { box-shadow: 0 0 0 3px var(--gold-hi) inset; background: rgba(255,225,150,.35); }
@media (max-width: 520px) {
  .st-cols { grid-template-columns: 1fr; }
}
/* หีบบนมือถือแนวนอน — an 844×390 phone gave the card 88vh ≈ 343px, and the
   title row + quantity row + column heads + the stash button ate all but ~2
   rows of it. Two rows behind a scrollbar is what reads as "เลื่อนไม่ได้" even
   though the grid scrolls perfectly: there is barely anything to scroll IN.
   Same layout, just less chrome around it — keep the two columns (the whole
   point of this screen is seeing both sides at once; the 520px stack rule above
   never applies in landscape anyway). */
@media (orientation: landscape) and (max-height: 520px) {
  #storage-modal { padding: 6px; }
  .st-card { max-height: 97vh; padding: 8px 12px; border-width: 3px; border-radius: 16px; }
  .st-top { margin-bottom: 4px; }
  .st-title { font-size: .95rem; }
  .st-close { width: 28px; height: 28px; }
  .st-tools { margin-bottom: 5px; gap: 6px; }
  .st-qlabel { font-size: .66rem; }
  .st-qbtn { padding: 3px 10px; font-size: .72rem; }
  .st-colhead { font-size: .74rem; margin-bottom: 3px; }
  .st-grid { grid-template-columns: repeat(auto-fill, minmax(44px, 1fr)); gap: 5px; padding: 5px; min-height: 60px; }
  .st-cell .st-ic { font-size: 1.15rem; }
  .st-cell .weap-wrap { width: 24px; height: 24px; }
  .st-cell .weap-wrap .weap-fb { font-size: 1.15rem; }
  .st-stash { margin-top: 5px; padding: 5px 12px; font-size: .72rem; }
}

/* ---------------- Win screen ---------------- */
#win-confetti { animation: bob 2s ease-in-out infinite; }

/* ---------------- animations ---------------- */
@keyframes bob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
@keyframes pop { from { transform: scale(.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes slideUp { from { transform: translateY(30px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@keyframes toastIn { from { transform: translateY(-14px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@keyframes toastOut { to { transform: translateY(-14px); opacity: 0; } }
@keyframes bar-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
@keyframes icon-shake { 0%,100% { transform: rotate(0deg); } 25% { transform: rotate(-12deg); } 75% { transform: rotate(12deg); } }
@keyframes questGlow {
  0%,100% { box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 16px rgba(120,85,45,.22), 0 0 16px rgba(255,206,92,.35); }
  50%     { box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 16px rgba(120,85,45,.22), 0 0 30px rgba(255,206,92,.7); }
}
@keyframes btnBounce {
  0%,100% { transform: scale(1) translateY(0); }
  40%     { transform: scale(1.05) translateY(-3px); }
  70%     { transform: scale(1.01) translateY(-1px); }
}
@keyframes celebPop { from { transform: scale(.5) translateY(50px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } }
@keyframes celebOut { to { transform: scale(.88) translateY(-24px); opacity: 0; } }
@keyframes tgPulse {
  0%,100% { box-shadow: 0 0 12px rgba(255,206,92,.4), 0 6px 14px rgba(120,85,45,.25); }
  50%     { box-shadow: 0 0 26px rgba(255,206,92,.75), 0 6px 14px rgba(120,85,45,.25); }
}
@keyframes rewardPop { from { transform: scale(.5) translateY(10px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } }

/* ---- Quest tracker: ready / claimable state ---- */
#quest-tracker.qt-ready {
  border-color: var(--gold);
  animation: questGlow 1.6s ease-in-out infinite;
}
#quest-tracker.qt-ready .qt-head {
  background: linear-gradient(180deg, rgba(255,206,92,.45), rgba(255,206,92,0));
}

/* ---- Goal progress bars inside the tracker (now inside .qt-bar-row) ---- */
.qt-progress {
  flex: 1; height: 5px;
  background: rgba(120,85,45,.18); border-radius: 999px; overflow: hidden;
}
.qt-bar {
  height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--gold), #ffe19a);
  transition: width .5s cubic-bezier(.4,0,.2,1);
}
.qt-goal-wrap.met .qt-bar { background: linear-gradient(90deg, var(--leaf-dark), var(--good)); }

/* ---- Reward preview strip ---- */
.qt-rewards-preview {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 14px 0; font-size: .76rem;
  border-top: 2px dashed var(--wood); margin-top: 7px;
}
.qt-rew-lbl { font-weight: 800; color: var(--gold-hi); font-size: .68rem; letter-spacing: .5px; }
.qt-rew-item { display: flex; align-items: center; gap: 2px; font-weight: 800; color: var(--gold-hi); }

/* ---- Direct claim button on tracker ---- */
.qt-claim-btn {
  display: block; margin: 8px 14px 2px; padding: 8px 14px; text-align: center;
  background: linear-gradient(180deg, #ffe19a, var(--gold));
  color: #6e4310; border: 2px solid #fff3d4; border-radius: 999px; font-weight: 800;
  font-size: .8rem; cursor: pointer; box-shadow: 0 3px 0 #c47a22;
  font-family: var(--font); letter-spacing: .5px;
  animation: btnBounce .75s ease-in-out infinite;
  pointer-events: auto;
}
.qt-claim-btn:active { transform: translateY(2px) !important; animation: none !important; box-shadow: 0 1px 0 #c47a22 !important; }

/* ---- Toolbar badge bounce ---- */
.tool-btn.badge { animation: btnBounce .85s ease-in-out infinite; }

/* ---- Quest panel: per-goal progress bars ---- */
.q-goal-row { padding: 3px 0; }
.q-goal-row .q-goal-text {
  display: flex; align-items: center; gap: 6px; font-size: .82rem; color: var(--ink-soft);
}
.q-goal-row .q-goal-text .tick { flex-shrink: 0; width: 18px; }
.q-goal-row.met .q-goal-text { color: var(--leaf-dark); font-weight: 700; }
.q-goal-row .q-bar-wrap { display: flex; align-items: center; gap: 6px; margin-left: 24px; margin-top: 2px; }
.q-goal-row .q-bar-track {
  flex: 1; height: 6px; background: rgba(120,85,45,.18); border-radius: 999px; overflow: hidden;
}
.q-goal-row .q-bar-fill {
  height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--gold), #ffe19a);
  transition: width .5s cubic-bezier(.4,0,.2,1);
}
.q-goal-row.met .q-bar-fill { background: linear-gradient(90deg, var(--leaf-dark), var(--good)); }
.q-goal-row .q-bar-num {
  font-size: .72rem; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--ink-soft); flex-shrink: 0; min-width: 28px; text-align: right;
}
.q-goal-row.met .q-bar-num { color: var(--leaf-dark); }

/* ---- Quest claim button in panel: bounce when available ---- */
.q-claim { animation: btnBounce .75s ease-in-out infinite; }
.q-claim:active { transform: translateY(2px); animation: none; box-shadow: 0 1px 0 #c47a22 !important; }

/* ---- Daily quests: tabs, timer, ready highlight, vector tick ---- */
.quest-tabs { margin-bottom: 12px; }
.quest.ready {
  border-left-color: var(--gold); box-shadow: 0 0 0 2px var(--gold-hi), 0 3px 0 rgba(138,95,51,.16);
  animation: questReadyGlow 1.6s ease-in-out infinite;
}
@keyframes questReadyGlow {
  0%, 100% { box-shadow: 0 0 0 2px rgba(245,167,63,.55), 0 3px 0 rgba(138,95,51,.16); }
  50%      { box-shadow: 0 0 0 3px rgba(245,167,63,1), 0 0 16px rgba(245,167,63,.5), 0 3px 0 rgba(138,95,51,.16); }
}
/* vector status dot (no decorative emoji) */
.qd-tick {
  display: inline-block; flex-shrink: 0; width: 15px; height: 15px; border-radius: 50%;
  border: 2.5px solid var(--wood); background: transparent; box-sizing: border-box; position: relative;
}
.qd-tick.on { border-color: var(--leaf-dark); background: var(--good); }
.qd-tick.on::after {
  content: ""; position: absolute; left: 3px; top: 0px; width: 4px; height: 8px;
  border: solid #fff; border-width: 0 2.2px 2.2px 0; transform: rotate(40deg);
}
.q-daily-tag {
  display: inline-block; margin-top: 8px; padding: 6px 14px; border-radius: 999px;
  font-weight: 800; font-size: .78rem; font-variant-numeric: tabular-nums;
  color: var(--ink-soft); background: rgba(120,85,45,.12); border: 2px solid rgba(120,85,45,.18);
}
.q-daily-tag.done { color: var(--leaf-dark); background: rgba(90,196,107,.16); border-color: rgba(90,196,107,.4); }
.daily-timer {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 0 0 12px; padding: 7px 10px; border-radius: 14px;
  background: rgba(120,85,45,.1); border: 2px solid rgba(120,85,45,.16);
  color: var(--ink-soft); font-weight: 700; font-size: .82rem;
}
.daily-timer b { font-variant-numeric: tabular-nums; color: var(--gold-hi); background: rgba(255,255,255,.6); border-radius: 8px; padding: 2px 9px; }
.daily-streak {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 0 0 8px; padding: 7px 10px; border-radius: 14px;
  background: linear-gradient(180deg, #fff6df, #ffe7ad); border: 2px solid var(--gold);
  color: #7a5410; font-weight: 800; font-size: .82rem;
}
.daily-streak b { color: #6e4310; background: rgba(255,255,255,.65); border-radius: 8px; padding: 2px 10px; }

/* ---- inline 7-day login calendar (Daily tab + popup) ---- */
.login-cal2 { margin: 0 0 10px; }
.lc2-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-bottom: 8px; }
.lc2-cell {
  position: relative; border: 2px solid var(--gold-lo); border-radius: 11px;
  padding: 6px 4px; background: rgba(255,255,255,.55); text-align: center;
}
.lc2-cell .lc2-day { font-size: .62rem; font-weight: 800; color: var(--wood-mid); }
.lc2-cell .lc2-rew { display: flex; flex-direction: column; gap: 1px; margin-top: 3px; font-size: .68rem; font-weight: 800; color: var(--ink); }
.lc2-cell.today { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(234,165,62,.4); background: linear-gradient(180deg, #fff6df, #ffedc2); }
.lc2-cell.done { opacity: .55; }
.lc2-check { position: absolute; top: 2px; right: 5px; color: #2f9e57; font-weight: 900; font-size: .8rem; }

/* day-7 grand prize — the wings, made to pop */
.lc2-grand {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 8px 12px 8px 6px; border-radius: 14px; overflow: hidden;
  border: 2.5px solid var(--gold);
  background: linear-gradient(110deg, #fff3cf 0%, #ffe49c 45%, #ffd770 100%);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 4px 14px rgba(224,165,46,.45);
}
.lc2-grand::after {
  content: ""; position: absolute; top: 0; left: -45%; width: 40%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.55), transparent);
  animation: lc2Sheen 3.2s ease-in-out infinite; pointer-events: none;
}
@keyframes lc2Sheen { 0% { left: -45%; } 60%,100% { left: 130%; } }
.lc2-grand.done { opacity: .6; filter: saturate(.8); }
.lc2-grand.done::after { animation: none; }
.lc2-wing-cv { width: 68px; height: 48px; flex: 0 0 auto; }
.lc2-grand-info { position: relative; z-index: 1; text-align: left; line-height: 1.25; }
.lc2-grand-tag { font-size: .6rem; font-weight: 900; letter-spacing: .4px; color: #b5781e; text-transform: uppercase; }
.lc2-grand-name { font-size: 1rem; font-weight: 900; color: #6e4310; }
.lc2-grand-sub { font-size: .66rem; font-weight: 800; color: #8a5a1e; margin-top: 1px; }
.lc2-grand-badge {
  position: absolute; top: 0; right: 0; z-index: 2;
  background: #e0463b; color: #fff; font-size: .56rem; font-weight: 900; letter-spacing: .5px;
  padding: 2px 7px; border-bottom-left-radius: 10px; border: 2px solid #fff3d4; border-top: 0; border-right: 0;
}

/* ---- Section headers (Daily / Weekly / Lifetime) ---- */
.q-section-head {
  display: flex; align-items: center; justify-content: space-between;
  margin: 16px 2px 8px; font-weight: 800; font-size: 1rem; color: var(--ink);
}
.q-section-head:first-of-type { margin-top: 4px; }
.q-section-meta {
  font-size: .68rem; font-weight: 700; color: var(--ink-soft);
  background: rgba(120,85,45,.1); border: 1.5px solid rgba(120,85,45,.16);
  border-radius: 999px; padding: 2px 9px;
}
/* rarity ribbon on a quest card title */
.q-rarity {
  margin-left: auto; font-size: .62rem; font-weight: 900; letter-spacing: .5px;
  padding: 2px 8px; border-radius: 999px; color: #fff; flex-shrink: 0;
}
.q-rarity.rare   { background: linear-gradient(160deg, #5aa8ff, #2f6fd6); box-shadow: 0 2px 0 #2f6fd6; }
.q-rarity.legend { background: linear-gradient(160deg, #ffd24a, #e89a1e); box-shadow: 0 2px 0 #c47a22; color: #5a3a00; }
/* rare / legend cards in the panel get a coloured edge */
.quest.rare   { border-left-color: #4aa8ff; }
.quest.legend { border-left-color: #f0a91e; background: linear-gradient(180deg, #fffdf2, #fdeecb); }
.quest.ready.legend {
  animation: questLegendGlow 1.5s ease-in-out infinite;
}
@keyframes questLegendGlow {
  0%, 100% { box-shadow: 0 0 0 2px rgba(240,169,30,.6), 0 3px 0 rgba(138,95,51,.16); }
  50%      { box-shadow: 0 0 0 3px rgba(240,169,30,1), 0 0 22px rgba(240,169,30,.55), 0 3px 0 rgba(138,95,51,.16); }
}

/* ---- Reward modal: rarity dressing + "wow" extras ---- */
.qc-card.qc-rare   { box-shadow: 0 0 0 3px #4aa8ff, 0 0 40px rgba(74,168,255,.5), 0 16px 40px rgba(0,0,0,.4); }
.qc-card.qc-legend { box-shadow: 0 0 0 3px #ffce4d, 0 0 60px rgba(255,206,77,.7), 0 16px 40px rgba(0,0,0,.45); }
.qc-rarity-banner {
  position: absolute; top: -14px; left: 50%; transform: translateX(-50%);
  font-weight: 900; font-size: .82rem; letter-spacing: 1px; color: #fff;
  padding: 6px 22px; border-radius: 12px; white-space: nowrap; z-index: 2;
}
.qc-rarity-banner.rare   { background: linear-gradient(160deg, #5aa8ff, #2f6fd6); box-shadow: 0 4px 0 #2f6fd6, 0 8px 14px rgba(0,0,0,.3); }
.qc-rarity-banner.legend { background: linear-gradient(160deg, #ffd24a, #e89a1e); color: #5a3a00; box-shadow: 0 4px 0 #c47a22, 0 8px 14px rgba(0,0,0,.3); animation: bannerPulse 1s ease-in-out infinite; }
@keyframes bannerPulse { 0%,100% { transform: translateX(-50%) scale(1); } 50% { transform: translateX(-50%) scale(1.06); } }
.qc-item.big .qc-item-icon { font-size: 2.4rem; }
.qc-flash.legend { background: radial-gradient(circle at 50% 45%, rgba(255,236,170,.95), rgba(255,206,77,.35) 45%, transparent 70%); }
.qc-confetti {
  position: fixed; width: 10px; height: 14px; border-radius: 3px;
  z-index: 101; pointer-events: none; will-change: transform, opacity;
}
/* "point" reward chips (XP / SP) — vector text badge, no emoji */
.qc-item-icon.qc-pt-ic {
  display: grid; place-items: center; width: 46px; height: 46px; border-radius: 50%;
  font-size: .9rem; font-weight: 900; color: #fff; letter-spacing: .5px;
  background: linear-gradient(160deg, #7b5cff, #5e3fe0); box-shadow: inset 0 -3px 0 rgba(0,0,0,.18);
}
.qc-item-icon.qc-pt-ic.sp { background: linear-gradient(160deg, #ffce4d, #e89a1e); color: #5a3a00; }

/* ---- Daily Missions — permanent MMORPG-style button, top-right beside the region bar ---- */
.tr-row { display: flex; align-items: center; gap: 10px; }
#daily-reward-btn {
  position: relative; z-index: 38;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  width: 60px; padding: 7px 4px 6px; cursor: pointer;
  border: 2.5px solid #fff3d4; border-radius: 16px;
  background: linear-gradient(180deg, #ffe19a, var(--gold)); color: #6e4310;
  font-family: var(--font); font-weight: 800;
  box-shadow: 0 4px 0 #c47a22, 0 7px 14px rgba(120,85,45,.30);
}
#daily-reward-btn.hidden { display: none; }
/* only call attention while a reward is actually waiting — idle = still */
#daily-reward-btn.ready { animation: drbFloat 2.4s ease-in-out infinite; }
#daily-reward-btn:hover { filter: brightness(1.04); }
#daily-reward-btn:active { transform: translateY(3px); box-shadow: 0 1px 0 #c47a22; }
#daily-reward-btn .drb-ic { width: 19px; height: 19px; color: #6e4310; }
#daily-reward-btn .drb-text { font-size: .66rem; letter-spacing: .3px; }
#daily-reward-btn .drb-day-wrap { display: flex; align-items: baseline; gap: 2px; line-height: 1; color: #8a5a1e; font-size: .5rem; font-weight: 900; text-transform: uppercase; }
#daily-reward-btn .drb-day-wrap b { font-size: .92rem; color: #6e4310; }
#daily-reward-btn .drb-glow {
  position: absolute; inset: -3px; border-radius: 18px; pointer-events: none;
  border: 2px solid #fff7c8; opacity: 0;
}
#daily-reward-btn.ready .drb-glow { animation: drbPulse 1.4s ease-out infinite; }
#daily-reward-btn .drb-count {
  position: absolute; top: -8px; right: -8px;
  display: grid; place-items: center; min-width: 22px; height: 22px; padding: 0 5px;
  border-radius: 999px; background: #e0463b; color: #fff; font-size: .78rem; font-weight: 900;
  border: 2px solid #fff3d4; box-shadow: 0 2px 5px rgba(0,0,0,.25);
  animation: drbBadge 1.3s ease-out infinite;
}
#daily-reward-btn .drb-count.hidden { display: none; }
@keyframes drbFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
@keyframes drbPulse { 0% { opacity: .7; transform: scale(1); } 100% { opacity: 0; transform: scale(1.35); } }
@keyframes drbBadge { 0%,100% { box-shadow: 0 0 0 0 rgba(224,70,59,.5); } 50% { box-shadow: 0 0 0 6px rgba(224,70,59,0); } }
@media (max-width: 560px) { .tr-row { gap: 7px; } #daily-reward-btn { width: 48px; padding: 5px 3px 4px; } #daily-reward-btn .drb-ic { width: 16px; height: 16px; } #daily-reward-btn .drb-text { font-size: .58rem; } }

/* day-7 grand prize cell in the login calendar */
#login-cal .lc-cell.grand { box-shadow: 0 0 0 2px var(--gold), 0 0 14px rgba(255,200,80,.5); }
#login-cal .lc-wing { color: #b5781e; font-weight: 900; }

/* ---- Quest complete: interactive reward modal ---- */
#quest-celebrate {
  position: fixed; inset: 0; z-index: 100; pointer-events: all;
  display: flex; align-items: center; justify-content: center;
  background: rgba(94,67,31,.38); backdrop-filter: blur(5px);
  animation: fadeInBg .35s ease;
}
@keyframes fadeInBg { from { opacity: 0; } to { opacity: 1; } }

.qc-card {
  background: linear-gradient(175deg, #fffcf0, #fae8c0);
  border: 3px solid var(--wood); border-radius: 28px;
  padding: 26px 30px 22px; text-align: center;
  min-width: 250px; max-width: 330px; width: 90vw;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 28px 64px rgba(120,85,45,.48);
  animation: celebPop .44s cubic-bezier(.2,1.4,.4,1) forwards;
  position: relative; overflow: hidden;
}
/* shimmer line at top */
.qc-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
  animation: shimmerSweep 2s ease infinite;
}
@keyframes shimmerSweep {
  0% { transform: translateX(-100%); } 100% { transform: translateX(100%); }
}

.qc-done {
  font-size: .7rem; font-weight: 800; color: var(--gold-hi);
  letter-spacing: 3.5px; text-transform: uppercase; margin-bottom: 6px;
}
.qc-star-row {
  font-size: 1.6rem; margin: 0 0 4px; line-height: 1;
  animation: starBounce .6s .3s cubic-bezier(.2,1.4,.4,1) both;
}
@keyframes starBounce { from { transform: scale(0) rotate(-20deg); opacity: 0; } to { transform: scale(1) rotate(0); opacity: 1; } }
.qc-name {
  font-size: 1.15rem; font-weight: 800; color: var(--ink);
  margin: 0 0 16px; line-height: 1.25;
}
.qc-sep { border: none; border-top: 2.5px dashed var(--wood); margin: 0 0 10px; }
.qc-rew-label {
  font-size: .67rem; font-weight: 800; color: var(--gold-hi);
  letter-spacing: 2px; text-transform: uppercase; margin-bottom: 10px;
}
.qc-rewards {
  display: flex; gap: 9px; justify-content: center; flex-wrap: wrap; margin-bottom: 20px;
}
.qc-item {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  background: #fff8ea; border: 2.5px solid var(--wood); border-radius: 16px;
  padding: 10px 12px; min-width: 54px;
  box-shadow: 0 3px 0 rgba(138,95,51,.22);
}
.qc-item-icon { font-size: 1.6rem; line-height: 1; }
.qc-item-qty { font-size: .78rem; font-weight: 800; color: var(--gold-hi); }
/* stagger-pop items in */
.qc-item:nth-child(1) { animation: rewardPop .35s .15s cubic-bezier(.2,1.4,.4,1) both; }
.qc-item:nth-child(2) { animation: rewardPop .35s .25s cubic-bezier(.2,1.4,.4,1) both; }
.qc-item:nth-child(3) { animation: rewardPop .35s .35s cubic-bezier(.2,1.4,.4,1) both; }
.qc-item:nth-child(4) { animation: rewardPop .35s .45s cubic-bezier(.2,1.4,.4,1) both; }
.qc-item:nth-child(5) { animation: rewardPop .35s .55s cubic-bezier(.2,1.4,.4,1) both; }

/* COLLECT button */
.qc-collect-btn {
  display: block; width: 100%; padding: 13px 24px;
  background: linear-gradient(180deg, #ffd271, #f5a73f);
  color: #6e4310; border: 2.5px solid #fff3d4; border-radius: 999px;
  font-weight: 800; font-size: 1.05rem; letter-spacing: 1.5px; text-transform: uppercase;
  cursor: pointer; box-shadow: 0 5px 0 #c47a22; font-family: var(--font);
  animation: btnBounce .8s .6s ease-in-out infinite both;
  transition: transform .08s, box-shadow .08s;
}
.qc-collect-btn:active { transform: scale(.88) translateY(4px) !important; box-shadow: 0 1px 0 #c47a22 !important; animation: none !important; }
.qc-collect-btn.btn-pressed { pointer-events: none; animation: btnPressOut .35s ease forwards !important; }
.qc-collect-btn:disabled { opacity: 0; animation: none; pointer-events: none; transition: opacity .2s; }
@keyframes btnPressOut {
  0%   { transform: scale(.88) translateY(4px); box-shadow: 0 1px 0 #c47a22; }
  35%  { transform: scale(1.06) translateY(-3px); box-shadow: 0 6px 0 #c47a22; }
  100% { transform: scale(1) translateY(0); opacity: 0; }
}

/* items fly-out — scale-burst then scatter outward */
@keyframes itemCollect {
  0%   { transform: scale(1);    opacity: 1; }
  18%  { transform: scale(1.55); opacity: 1; }
  55%  { transform: scale(.9) translate(var(--fx,0px), var(--fy,-55px)); opacity: .9; }
  100% { transform: scale(.3) translate(var(--fx2,0px), var(--fy2,-100px)); opacity: 0; }
}
.qc-rewards.collecting .qc-item { animation: itemCollect .58s cubic-bezier(.2,.8,.4,1) forwards; }
.qc-rewards.collecting .qc-item:nth-child(1) { animation-delay:0s;   --fx:-20px;--fy:-50px;--fx2:-35px;--fy2:-110px; }
.qc-rewards.collecting .qc-item:nth-child(2) { animation-delay:.09s; --fx:0px; --fy:-60px;--fx2:0px; --fy2:-120px; }
.qc-rewards.collecting .qc-item:nth-child(3) { animation-delay:.18s; --fx:20px;--fy:-50px;--fx2:35px; --fy2:-110px; }
.qc-rewards.collecting .qc-item:nth-child(4) { animation-delay:.27s; --fx:-15px;--fy:-45px;--fx2:-25px;--fy2:-105px; }
.qc-rewards.collecting .qc-item:nth-child(5) { animation-delay:.36s; --fx:15px;--fy:-45px;--fx2:25px; --fy2:-105px; }

/* screen flash on collect */
.qc-flash {
  position: fixed; inset: 0; background: #fff8ea;
  pointer-events: none; z-index: 200;
  animation: screenFlash .45s ease forwards;
}
@keyframes screenFlash {
  0%   { opacity: 0; }
  12%  { opacity: .5; }
  100% { opacity: 0; }
}

/* gold sparkle particles */
.qc-sparkle {
  position: fixed; border-radius: 50%; pointer-events: none; z-index: 201;
  transform: translate(-50%, -50%);
  animation: sparkleBurst .62s ease forwards;
}
@keyframes sparkleBurst {
  0%   { transform: translate(-50%,-50%) scale(1.2); opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx,0px)), calc(-50% + var(--dy,0px))) scale(0); opacity: 0; }
}

/* card gold-pulse then exit */
@keyframes cardGoldFlash {
  0%,100% { border-color: var(--wood); }
  35%     { border-color: var(--gold); box-shadow: inset 0 0 0 2px #fff8ea, 0 28px 64px rgba(120,85,45,.5), 0 0 50px rgba(234,165,62,.6); }
}
.qc-card.collecting { animation: cardGoldFlash .55s ease; }
.qc-out { animation: celebOut .38s ease forwards; }

/* ---- Tutorial guide pointer ---- */
#tutorial-guide {
  position: fixed; z-index: 90; pointer-events: none;
  transition: opacity .3s ease;
}
.tg-bubble {
  background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 2.5px solid var(--wood); border-radius: 14px;
  padding: 7px 14px; font-size: .8rem; font-weight: 800;
  color: var(--wood-dark); white-space: nowrap;
  animation: tgPulse 1.5s ease-in-out infinite;
}
.tg-arrow-down {
  width: 0; height: 0; margin: 0 auto;
  border-left: 9px solid transparent; border-right: 9px solid transparent;
  border-top: 12px solid var(--wood);
}
.tg-arrow-up {
  width: 0; height: 0; margin: 0 auto;
  border-left: 9px solid transparent; border-right: 9px solid transparent;
  border-bottom: 12px solid var(--wood);
}
.tg-arrow-left {
  width: 0; height: 0; flex-shrink: 0;
  border-top: 9px solid transparent; border-bottom: 9px solid transparent;
  border-right: 12px solid var(--wood);
}
#tutorial-guide.tg-side { display: flex; align-items: center; gap: 4px; }

/* ---- Quest compass — chevron orbiting the hero, points at the next place-goal ---- */
#quest-compass {
  position: fixed; left: 0; top: 0; width: 0; height: 0;
  z-index: 14; pointer-events: none;
}
#quest-compass .qc-arrow {
  position: absolute; left: -13px; top: -13px; width: 26px; height: 26px;
  stroke: #ffd75e; stroke-width: 3.4;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.75));
  animation: qcPulse 1.1s ease-in-out infinite;
}
#quest-compass .qc-name {
  position: absolute; left: 0; top: 16px; transform: translateX(-50%);
  white-space: nowrap; font-size: .68rem; font-weight: 800; color: #ffe9a8;
  background: rgba(20, 24, 32, .74); padding: 2px 8px; border-radius: 9px;
  border: 1px solid rgba(255, 215, 94, .45);
}
/* arrow points up (over the hero's name tag) — chip goes above it instead */
#quest-compass.qc-up .qc-name { top: auto; bottom: 16px; }
#quest-compass.qc-up .qc-close { top: 8px; left: 15px; }
#quest-compass .qc-close {
  position: absolute; top: -15px; left: 15px;
  width: 17px; height: 17px; border-radius: 50%;
  background: rgba(20, 24, 32, .85); border: 1px solid rgba(255, 215, 94, .45);
  color: #ffe9a8; font-size: .64rem; font-weight: 900; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  padding: 0; cursor: pointer; pointer-events: auto;
  transition: background .15s, color .15s;
}
#quest-compass .qc-close:hover { background: rgba(60, 20, 20, .9); color: #ff8a8a; }
@keyframes qcPulse { 0%, 100% { opacity: .7; } 50% { opacity: 1; } }

/* a freshly unlocked toolbar button glows until pressed (see refreshToolbarUnlocks) */
.tool-btn.tb-just {
  animation: tbJust 1s ease-in-out infinite;
  box-shadow: 0 0 0 2px rgba(111, 211, 255, .9), 0 0 14px 4px rgba(111, 211, 255, .55);
}
@keyframes tbJust { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.12); } }

/* ---------------- responsive ---------------- */
@media (max-width: 820px) {
  #bottom-bar { right: 14px; }
  .player-meta { min-width: 150px; }
  .bar { width: 150px; }
}
@media (max-width: 620px) {
  #chat-box { width: min(240px, 62vw); bottom: 88px; padding: 6px 10px; }
  #chat-log { max-height: 96px; font-size: .74rem; }
  .portrait-frame { width: 74px; height: 74px; }
  #hud-portrait { width: 62px; height: 62px; top: 6px; left: 6px; }
  .bar { width: 120px; height: 14px; }
  #side-nav { gap: 7px; }
  #side-nav .nav-btn { width: 52px; height: 52px; }
  #side-nav .nav-btn .ic { width: 1.4rem; height: 1.4rem; }
  #minimap { width: 92px; height: 92px; }
  #region-name { font-size: .82rem; }
  #quest-tracker { width: 200px; top: 8px; }
  .qt-name { font-size: .85rem; }
  .qt-goal { font-size: .76rem; }
  #dock { gap: 6px; padding: 6px 8px; }
  .tool-btn { width: 40px; height: 40px; }
  .tool-btn .ic { width: 1.25rem; height: 1.25rem; }
  .hb-slot { width: 40px; height: 46px; }
  .hb-slot .hb-icon { font-size: 1.1rem; }
  #hotbar { gap: 3px; }
  #action-btn { width: 70px; height: 70px; right: 14px; bottom: 14px; }
  .ab-icon { width: 1.8rem; height: 1.8rem; }
  .dialogue-portrait { width: 56px; height: 56px; font-size: 2.2rem; }
  #system-log { max-width: 200px; }
  #bottom-bar-fill { max-width: 200px; }
}
@media (max-width: 420px) {
  #player-hud { gap: 8px; }
  .player-meta { min-width: 120px; }
  .bar { width: 100px; height: 12px; }
  #dock { gap: 4px; padding: 5px 6px; }
  .hb-slot { width: 34px; height: 40px; }
  .hb-slot .hb-icon { font-size: .95rem; }
  #hotbar { gap: 2px; }
  #system-log .sl-status { display: none; }
}

/* ---------------- Touch devices (phones / tablets) ----------------
   Set once by UI._setupTouch(). Keeps every control clear of notches and
   the home-bar, and lifts the action button up to right-thumb reach so it
   is no longer crammed into the corner next to the dock. */
/* Mobile profile: compact column — portrait on top, slim bars below.
   Keeps the left side under ~90px wide so it never overlaps the
   quest-tracker card at the top-right. */
body.touch-mode #player-hud {
  top: calc(10px + env(safe-area-inset-top));
  left: calc(10px + env(safe-area-inset-left));
  flex-direction: column;
  align-items: flex-start;
  gap: 5px;
}
/* Compact portrait */
body.touch-mode .portrait-frame { width: 76px; height: 76px; }
body.touch-mode #hud-portrait   { top: 5px; left: 5px; width: 66px; height: 66px; }
body.touch-mode .portrait-frame .lvl-badge { min-width: 22px; height: 22px; font-size: .72rem; top: -2px; right: -4px; }
/* Slim stats */
body.touch-mode .player-meta    { padding-top: 0; min-width: 0; gap: 4px; }
body.touch-mode .stat           { gap: 5px; }
body.touch-mode .stat-icon      { width: .85rem; height: .85rem; }
body.touch-mode .bar            { width: 88px; height: 12px; }
body.touch-mode .bar .stat-val  { font-size: .56rem; }
body.touch-mode #top-right  { top: calc(12px + env(safe-area-inset-top)); right: calc(14px + env(safe-area-inset-right)); }
body.touch-mode #daily-reward-btn { display: none; }
body.touch-mode #bottom-bar {
  left: calc(14px + env(safe-area-inset-left));
  right: calc(14px + env(safe-area-inset-right));
  bottom: calc(6px + env(safe-area-inset-bottom));
}
body.touch-mode #action-btn {
  display: flex;
  right: calc(24px + env(safe-area-inset-right));
  bottom: calc(118px + env(safe-area-inset-bottom));
}
body.touch-mode #prompt-row {
  bottom: calc(176px + env(safe-area-inset-bottom));
}
body.touch-mode #dialogue {
  bottom: calc(220px + env(safe-area-inset-bottom));
  padding: 0 12px;
}

/* On touch/mobile: show the side-nav collapsed by default (JS adds .collapsed).
   Repositioned to stay clear of the joystick at the bottom. */
body.touch-mode #side-nav {
  display: flex;
  top: auto;
  bottom: calc(260px + env(safe-area-inset-bottom));
  transform: none;
  left: 10px;
  pointer-events: all;
}
/* #system-log has no z-index and grows freely to fit Thai text (no clamp —
   a clipped line lands mid-word), so a multi-line hint can stretch down into
   this fan-arc and, sitting later in the DOM, paint over + steal taps from
   these buttons. Nothing here should shrink the log; the buttons just need
   to always win the stack. */
body.touch-mode #side-nav .nav-btn {
  z-index: 2;
  width: 52px; height: 52px; gap: 0; border-radius: 50%;
  /* soft translucent disc so the icon reads against any background,
     without stealing attention from the world (user: "ให้เห็น แต่ไม่ต้องชัดมาก") */
  background: rgba(30, 22, 14, .42);
  border: 1.5px solid rgba(255, 248, 234, .35);
  box-shadow: 0 2px 6px rgba(0,0,0,.3);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
body.touch-mode #side-nav .nav-btn .ic {
  width: 2rem; height: 2rem;
  color: #fff8ea;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.6));
}
body.touch-mode #side-nav .nav-btn label { display: none; }
body.touch-mode #nav-collapse { width: 30px; height: 30px; font-size: 1rem; }

/* .touch-only buttons appear only in touch mode (hidden on desktop). */
.touch-only { display: none; }
body.touch-mode .tool-btn.touch-only { display: flex; }
/* .no-touch is the inverse: hidden on touch. */
body.touch-mode .tool-btn.no-touch { display: none; }
body.touch-mode .sl-close { display: block; }

/* Keyboard-only labels have no meaning on touch */
body.touch-mode .tk    { display: none; }   /* M / Q / ESC labels in toolbar */
body.touch-mode .hb-key { display: none; }  /* 1–9 slot-number labels in hotbar */
body.touch-mode #interact-prompt kbd { display: none; } /* the E keycap in the interact pill */

/* Build / Craft / Inventory already live on the side-nav (both desktop and
   touch); Character folds into the top-left portrait tap on touch — so the
   toolbar carries Skill / Map / Quests / Journal / Camera zoom / Menu on
   touch (plus the collapse chevron, #toolbar-collapse above). */
body.touch-mode #toolbar .tool-btn[data-panel="character"] { display: none; }

/* Phones show fewer hotbar slots (6, built in UI._buildHotbar) — so make each
   one big and thumb-friendly. clamp() keeps all six on one row from small
   phones up to large ones without overflowing the dock. */
body.touch-mode #hotbar { gap: clamp(5px, 1.6vw, 8px); }
body.touch-mode .hb-slot {
  width: clamp(44px, 14vw, 58px);
  height: clamp(50px, 16vw, 64px);
  border-radius: 16px;
}
body.touch-mode .hb-slot .hb-icon { font-size: clamp(1.2rem, 5vw, 1.7rem); }
body.touch-mode .hb-slot .hb-qty  { font-size: .66rem; }
body.touch-mode #weap-lock {
  width: clamp(38px, 11vw, 46px);
  height: clamp(50px, 16vw, 64px);
  border-radius: 16px;
}
body.touch-mode #weap-lock .wl-ic { width: 22px; height: 22px; }

/* ---- Touch: shrink the quest card to ~half size ----
   Cuts the non-essential reward-icon preview row and the goal "chips",
   tightens every padding/font, and narrows the card. The goal list (the
   one thing players actually track on the move) stays and scrolls if long.
   Rewards are still fully visible in the slide-out Quests panel. */
@media (max-width: 480px) {
  body.touch-mode #quest-tracker { width: 168px; }
}
body.touch-mode #quest-tracker {
  top: calc(68px + env(safe-area-inset-top)); /* fallback; JS _syncQuestTrackerTop() overrides this */
  right: calc(10px + env(safe-area-inset-right));
  width: 178px;
  border-width: 2px; border-radius: 14px; padding-bottom: 6px;
  max-height: calc(100vh - 230px);
  display: flex; flex-direction: column;
}
/* non-essential — drop the reward preview row entirely on phones */
body.touch-mode #quest-tracker .qt-rewards-preview { display: none; }
/* tighter chrome */
body.touch-mode #quest-tracker .qt-head { padding: 7px 10px; }
body.touch-mode #quest-tracker .qt-label { font-size: .68rem; }
body.touch-mode #quest-tracker .qt-name { font-size: .8rem; margin: 6px 10px 3px; }
body.touch-mode #quest-tracker .qt-goals {
  gap: 5px; padding: 4px 10px 2px;
  overflow-y: auto; flex: 1 1 auto; -webkit-overflow-scrolling: touch;
}
body.touch-mode #quest-tracker .qt-goal-line { font-size: .74rem; gap: 5px; }
body.touch-mode #quest-tracker .qt-val { font-size: .72rem; }
body.touch-mode #quest-tracker .qt-tick { width: 13px; height: 13px; }
body.touch-mode #quest-tracker .qt-bar-row { padding-left: 18px; }
body.touch-mode #quest-tracker .qt-claim-btn { margin: 6px 10px 0; padding: 6px; font-size: .72rem; }
/* When tucked away the half-size rules must not fight the slide-out tab */
body.touch-mode #quest-tracker.qt-minimized {
  top: calc(50% - 22px); right: 0; width: auto; max-height: none; display: block;
}

/* ---- Quest tracker: frameless floating text (ALL platforms) ----
   The card background blocked too much of the screen, so the open tracker
   sheds its box entirely — white text with a warm dark shadow reads over
   any biome, like classic cozy-game quest HUDs. Desktop follows the mobile
   layout on request (same look everywhere, rewards row hidden — rewards
   are still shown in the Quests panel). The minimised slide-out tab keeps
   its solid chrome (it must look tappable). */
#quest-tracker:not(.qt-minimized) {
  background: none;
  border-color: transparent;
  box-shadow: none;
}
#quest-tracker:not(.qt-minimized) .qt-head {
  background: none;
  border-bottom: none;
  padding-bottom: 0;
}
#quest-tracker:not(.qt-minimized):hover { filter: none; }
#quest-tracker .qt-rewards-preview { display: none; }
#quest-tracker .qt-label,
#quest-tracker .qt-name,
#quest-tracker .qt-goal-line,
#quest-tracker .qt-val,
#quest-tracker .qt-claim {
  color: #fff;
  text-shadow: 0 1px 2px rgba(70,45,15,.95), 0 0 6px rgba(70,45,15,.5);
}
#quest-tracker .qt-icon,
#quest-tracker .qt-toggle,
#quest-tracker .qt-close {
  color: #fff3d8;
  filter: drop-shadow(0 1px 2px rgba(70,45,15,.8));
}
/* met goals stay green, but bright enough to sit on the shadowed text */
#quest-tracker .qt-goal-wrap.met .qt-goal-line,
#quest-tracker .qt-goal-wrap.met .qt-val { color: #c4f5a6; }
#quest-tracker .qt-chip.ok   { color: #c4f5a6; text-shadow: 0 1px 2px rgba(70,45,15,.95); }
#quest-tracker .qt-chip.miss { color: #ffc2b0; text-shadow: 0 1px 2px rgba(70,45,15,.95); }
#quest-tracker .qt-goto-arrow { color: #ffd271; text-shadow: 0 1px 2px rgba(70,45,15,.95); }
/* the where-to-find line reads over the world too — same white + shadow as the
   goal text, one notch dimmer so it stays a footnote to the goal above it */
#quest-tracker .qt-src { color: #ffeccb; opacity: .92; text-shadow: 0 1px 2px rgba(70,45,15,.95); }
/* desktop goal text also wraps like mobile instead of ellipsizing */
#quest-tracker .qt-goal-text {
  white-space: normal;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

/* On small phones move system log beside the player profile (top-left) */
@media (max-width: 620px) {
  body.touch-mode #system-log {
    display: flex;
    position: fixed;
    /* below the stat bars — #stat-panel's bottom lands at ~180px on a 390px
       viewport (measured); stamina is drawn under the player in-world now,
       not as a 4th row here, so #stat-panel is back to health/hunger/thirst. */
    top: calc(188px + env(safe-area-inset-top));
    left: calc(10px + env(safe-area-inset-left));
    right: auto; bottom: auto;
    flex: none;
    max-width: 160px;
    padding: 6px 10px;
    font-size: .74rem;
    border-radius: 16px;
    border-width: 2px;
  }
  body.touch-mode #system-log .sl-status { display: none; }
  body.touch-mode #bottom-bar-fill { display: none; }
}

/* ============================================================
   Portrait phones — polish the bottom controls.
   Scoped to touch + portrait so the web/desktop build (no
   .touch-mode class) and landscape layouts stay exactly as-is.
   Fixes: the dock used to overflow the viewport, shoving the
   toolbar (Character / Build / Menu) off the right edge so it
   was unreachable on a portrait phone.
   ============================================================ */
@media (orientation: portrait) {
  /* Cap the dock to the bar width and let it wrap into tidy,
     centred rows instead of running off-screen. */
  body.touch-mode #bottom-bar { justify-content: center; }
  body.touch-mode #dock {
    max-width: 100%;
    flex-wrap: wrap-reverse;   /* keep the item hotbar on the bottom row, shortcuts above */
    justify-content: center;
    padding: 0 0 4px;
    row-gap: 10px;
    /* frameless like landscape — the rows float straight on the world */
    background: none;
    border: none;
    box-shadow: none;
  }
  /* The vertical divider only reads correctly on a single row. */
  body.touch-mode #dock .dock-divider { display: none; }

  /* Trim the hotbar slots a touch in portrait so the six-slot row never
     stretches edge-to-edge — leaves a clear gutter inside the dock. */
  body.touch-mode #hotbar { gap: 6px; }
  body.touch-mode .hb-slot {
    width: clamp(40px, 12.5vw, 52px);
    height: clamp(46px, 14.5vw, 58px);
  }

  /* Lift the thumb controls clear of the now two-row dock. */
  body.touch-mode #joystick        { bottom: calc(150px + env(safe-area-inset-bottom)); }
  body.touch-mode #action-btn      { bottom: calc(158px + env(safe-area-inset-bottom)); }
  body.touch-mode #dash-btn        { bottom: calc(158px + env(safe-area-inset-bottom)); right: calc(106px + env(safe-area-inset-right)); }
  body.touch-mode #jump-btn        { bottom: calc(248px + env(safe-area-inset-bottom)); right: calc(12px + env(safe-area-inset-right)); }
  /* stacked above the jump button, in the RIGHT-most column — the 106px column
     is already move/cook's, and those pop in and out mid-play */
  body.touch-mode #playdead-btn    { bottom: calc(312px + env(safe-area-inset-bottom)); right: calc(12px + env(safe-area-inset-right)); }
  body.touch-mode #guard-btn       { bottom: calc(376px + env(safe-area-inset-bottom)); right: calc(12px + env(safe-area-inset-right)); }
  body.touch-mode #move-btn        { bottom: calc(232px + env(safe-area-inset-bottom)); right: calc(106px + env(safe-area-inset-right)); }
  body.touch-mode #cook-btn        { bottom: calc(304px + env(safe-area-inset-bottom)); right: calc(106px + env(safe-area-inset-right)); }
  body.touch-mode #prompt-row { bottom: calc(240px + env(safe-area-inset-bottom)); }

  /* Lift dialogue clear of the action button (158px bottom + 70px height = 228px top).
     Also cap height so very long story text never overflows past the top HUD. */
  body.touch-mode #dialogue {
    bottom: calc(268px + env(safe-area-inset-bottom));
    max-height: calc(100dvh - 300px - env(safe-area-inset-top) - env(safe-area-inset-bottom));
    overflow: hidden;
    align-items: flex-end;
  }
  body.touch-mode .dialogue-box {
    max-height: 100%;
    overflow: hidden;
    display: flex;
  }
  body.touch-mode .dialogue-text {
    max-height: 34vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* ============================================================
   Landscape phones — the viewport is wide but very short
   (~300–390px tall in Safari), so the portrait stack doesn't fit.
   Layout: joystick bottom-left, action cluster bottom-right, the
   dock centred between them, system log as a top-centre ticker.
   Scoped to touch + landscape + short screens so desktop windows
   and iPads (tall even in landscape) are untouched.
   ============================================================ */
@media (orientation: landscape) and (max-height: 520px) {
  /* Wide screen → the profile block goes back to the desktop row shape:
     portrait on the left, coins + survival bars beside it (portrait mode
     stacks them in a column because there's no width to spare there). */
  body.touch-mode #player-hud {
    flex-direction: row; align-items: flex-start; gap: 8px;
    top: calc(4px + env(safe-area-inset-top));
    left: calc(4px + env(safe-area-inset-left));
  }
  /* coin chip tucks under the portrait; the survival bars keep the column */
  body.touch-mode #coin-chip { position: absolute; left: 0; top: 82px; }
  body.touch-mode #top-right {
    top: calc(4px + env(safe-area-inset-top));
    right: calc(4px + env(safe-area-inset-right));
  }

  /* Thumb controls hug the corners — the short screen has no height to
     spare, so every px goes to the world. env() keeps them off the notch
     and the home indicator. */
  body.touch-mode #joystick   { bottom: calc(6px + env(safe-area-inset-bottom)); left: calc(6px + env(safe-area-inset-left)); }

  /* Right thumb cluster — genre-standard action layout: the big attack
     button owns the corner and the satellites (dash → move → jump) fan
     on one ~105px arc around its centre at 15° / 50° / 85°, all the
     same size so the hand learns one shape. */
  /* +16px on every bottom vs the corner so the big action button's centre sits
     level with the joystick's centre (the stick is 120px, the action 88px — at
     an equal bottom the taller stick reads as "higher"). The satellite arc rides
     up the same 16px so its fan shape around the action centre is unchanged. */
  body.touch-mode #action-btn { bottom: calc(22px + env(safe-area-inset-bottom)); right: calc(6px + env(safe-area-inset-right)); }
  body.touch-mode #dash-btn {
    width: 56px; height: 56px;
    bottom: calc(64px + env(safe-area-inset-bottom));
    right: calc(122px + env(safe-area-inset-right));
  }
  body.touch-mode #move-btn {
    width: 56px; height: 56px;
    bottom: calc(112px + env(safe-area-inset-bottom));
    right: calc(84px + env(safe-area-inset-right));
  }
  body.touch-mode #jump-btn {
    width: 56px; height: 56px;
    bottom: calc(142px + env(safe-area-inset-bottom));
    right: calc(30px + env(safe-area-inset-right));
  }
  /* cook continues the same arc one step further round, past jump */
  body.touch-mode #cook-btn {
    width: 56px; height: 56px;
    bottom: calc(188px + env(safe-area-inset-bottom));
    right: calc(6px + env(safe-area-inset-right));
  }
  /* play dead sits directly BELOW dash on the same column (same `right`), so
     the two read as one vertical pair instead of a third arc position */
  body.touch-mode #playdead-btn {
    width: 56px; height: 56px;
    bottom: calc(4px + env(safe-area-inset-bottom));
    right: calc(122px + env(safe-area-inset-right));
  }
  /* the shield sits between the jump and dash buttons, on its own outer arc
     so it clears move-btn's inner slot (still inside the same thumb roll) */
  body.touch-mode #guard-btn {
    width: 56px; height: 56px;
    bottom: calc(168px + env(safe-area-inset-bottom));
    right: calc(128px + env(safe-area-inset-right));
  }
  body.touch-mode #move-btn .mb-icon { width: 1.4rem; height: 1.4rem; }
  body.touch-mode #move-btn .mb-label { font-size: .5rem; }
  body.touch-mode #cook-btn .cb-icon { width: 1.4rem; height: 1.4rem; }
  body.touch-mode #cook-btn .cb-label { font-size: .5rem; }

  /* Dock sits centred in the strip between the two thumb clusters.
     Slots shrink a touch and the bar may wrap to two tidy rows on
     narrower phones (SE-class) — same trick as portrait. */
  body.touch-mode #bottom-bar {
    left: calc(150px + env(safe-area-inset-left));
    right: calc(150px + env(safe-area-inset-right));
    bottom: calc(2px + env(safe-area-inset-bottom));
    justify-content: center;
  }
  body.touch-mode #bottom-bar-fill { display: none; }
  body.touch-mode #dock {
    max-width: 100%;
    flex-wrap: wrap-reverse;
    justify-content: center;
    padding: 0 0 4px;
    row-gap: 8px;
    /* frameless — the slots float straight on the world, no cream tray */
    background: none;
    border: none;
    box-shadow: none;
  }
  body.touch-mode #dock .dock-divider { display: none; }
  body.touch-mode #hotbar { gap: 8px; }
  body.touch-mode .hb-slot {
    width: clamp(46px, 8vw, 64px);
    height: clamp(50px, 7vw, 58px);
  }
  body.touch-mode #dock .tool-btn { width: 40px; height: 40px; }
  body.touch-mode #dock .tool-btn .ic { width: 1.25rem; height: 1.25rem; }

  /* Menu shortcuts leave the dock: a single row on the top edge, just
     left of the region bar. The dock keeps only the item hotbar, so the
     centre strip stays a single slim row. */
  /* Menu row rides the SAME top line as the region bar, just to its LEFT. The
     region pill is right-aligned and its width tracks the (variable) zone name,
     so its exact `right` offset is set at runtime by _syncToolbarLeft() (anchors
     to #top-right's measured left edge). The values here are only a first-paint
     fallback before that runs. Since mobile toasts are off, the top-centre strip
     is free for this. Toasts no longer share this row. */
  body.touch-mode #toolbar {
    position: fixed;
    top: calc(4px + env(safe-area-inset-top));
    right: calc(210px + env(safe-area-inset-right));
    display: flex;
    gap: 8px;
  }

  /* System log leaves the bottom bar and tucks under the coin chip in
     the left profile column; it auto-fades a few seconds after the last
     message like everywhere else on touch. Time/phase is already in the
     region bar, so drop the duplicate. */
  body.touch-mode #system-log {
    position: fixed;
    top: calc(122px + env(safe-area-inset-top)); /* below portrait + coin chip */
    left: calc(4px + env(safe-area-inset-left));
    right: auto; bottom: auto;
    transform: none;
    flex: none;
    max-width: 200px;
    padding: 5px 12px;
    font-size: .72rem;
    border-radius: 14px;
    border-width: 2px;
  }
  body.touch-mode #system-log .sl-status { display: none; }

  /* The side-nav (build / craft / bag) fans in an arc hugging the
     joystick's top-right rim — every shortcut one thumb-roll away.
     Radius ≈115px from the stick's centre (80,76): build at the top,
     craft on the diagonal, bag out by the right rim. Duplicates are
     dropped from the top menu row below. */
  body.touch-mode #side-nav {
    top: auto;
    transform: none;
    left: calc(4px + env(safe-area-inset-left));
    bottom: calc(142px + env(safe-area-inset-bottom));
  }
  body.touch-mode #side-nav .nav-btn { position: fixed; }
  body.touch-mode #side-nav .nav-btn[data-panel="building"] {
    left: calc(21px + env(safe-area-inset-left));
    bottom: calc(154px + env(safe-area-inset-bottom));
  }
  body.touch-mode #side-nav .nav-btn[data-panel="crafting"] {
    left: calc(107px + env(safe-area-inset-left));
    bottom: calc(135px + env(safe-area-inset-bottom));
  }
  body.touch-mode #side-nav .nav-btn[data-panel="inventory"] {
    left: calc(154px + env(safe-area-inset-left));
    bottom: calc(61px + env(safe-area-inset-bottom));
  }
  /* the collapse chevron has no row to collapse any more */
  body.touch-mode #nav-collapse { display: none; }

  /* Interact pill above the dock, dialogue anchored to the bottom
     (it overlays the dock while talking — you aren't using the
     hotbar mid-conversation, and the height simply isn't there). */
  body.touch-mode #prompt-row { bottom: calc(96px + env(safe-area-inset-bottom)); }
  body.touch-mode #dialogue {
    bottom: calc(6px + env(safe-area-inset-bottom));
    max-height: calc(100dvh - 20px - env(safe-area-inset-top));
    overflow: hidden;
    align-items: flex-end;
  }
  body.touch-mode .dialogue-box { max-height: 100%; overflow: hidden; display: flex; }
  body.touch-mode .dialogue-text {
    max-height: 30vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* No corner minimap on touch (the Map button opens the full map);
     the quest tracker pins below the region bar (JS) and is capped so
     it can't run into the jump/action cluster. */
  body.touch-mode #quest-tracker { max-height: calc(100dvh - 230px); }
  /* minimised slide-out tab rides higher so it never sits on the jump button */
  body.touch-mode #quest-tracker.qt-minimized { top: calc(30% - 22px); right: 0; }

  /* ---- polish ---- */
  /* Pop-up panels: use the width the short screen has plenty of (more
     crafting/inventory columns per row) and nearly all of its height —
     the 86vh default left a sliver of world peeking under the sheet. */
  body.touch-mode .panel {
    max-width: min(760px, calc(100vw - 24px));
    max-height: calc(100dvh - 16px);
  }
  /* Single-column panels (quests/menu/crafting/building/shop/map) don't have
     grid content to reflow into the extra width — stay near the desktop
     width and let the taller max-height carry the extra vertical room. */
  body.touch-mode .panel.panel-narrow {
    max-width: min(560px, calc(100vw - 24px));
  }
  /* Character panel — the stage-on-top + drawer-below split assumes portrait
     height: on a short landscape screen the fixed stage swallows the whole
     panel, the XP bar gets cut and the drawer (the only scrollable pane)
     ends up a few px tall — it reads as "can't scroll". Put the panes
     side-by-side instead: stage as a fixed left column, drawer scrolling
     on the right, panel taking the full screen height. */
  body.touch-mode .panel.panel-tall { height: calc(100dvh - 16px); }
  body.touch-mode .panel.panel-inv { height: calc(100dvh - 16px); }
  body.touch-mode #panel-body.pb-flush { padding: 0; } /* generic 12px rule below would un-flush the panes */
  body.touch-mode #panel-body.pb-inv { padding: 0; }   /* same: keep the inv panes flush */
  body.touch-mode .char-screen { flex-direction: row; }
  body.touch-mode .char-stage {
    flex: 0 0 322px; min-height: 0; overflow-y: auto;
    padding: 12px 14px;
    border-bottom: none; border-right: 3px solid var(--wood);
    box-shadow: 6px 0 12px -6px rgba(120,85,45,.35);
  }
  body.touch-mode .char-stage .char-portrait canvas { width: 100px; height: 115px; }
  body.touch-mode .char-drawer { min-width: 0; }
  body.touch-mode .panel-header { padding: 10px 20px; }
  body.touch-mode .panel-header h2 { font-size: 1.15rem; }
  body.touch-mode #panel-body { padding: 12px 20px; }

  /* ---- Backpack on a short landscape screen ----
     The stacked detail footer (a text block, then a full-width row of three
     slab buttons) ate ~135px — more than the item grid itself, so barely one
     row of items stayed visible, and a weapon card (stats + source + the
     forges-into ladder) pushed the grid off-screen entirely.

     This block trims the chrome and lays the footer on its SIDE (icon + text
     left, compact action pills right) — the layout for narrow landscape
     phones. Wider ones get the two-pane version in the next media block.
     CSS only: the panel rebuilds its innerHTML on a 0.4s timer, so a JS
     layout switch would risk eating a tap mid-rebuild. */
  body.touch-mode .inv-head { padding: 5px 14px; gap: 8px; }
  body.touch-mode .inv-head-title { font-size: 1rem; }
  body.touch-mode .inv-head .inv-coin-row { padding: 2px 12px 2px 5px; }
  body.touch-mode .inv-head .inv-coin-val { font-size: .88rem; }
  body.touch-mode .inv-top { padding: 5px 14px 2px; }
  body.touch-mode .inv-hrow { margin-bottom: 5px; gap: 5px 10px; }
  body.touch-mode .inv-search { padding: 6px 12px 6px 30px; font-size: .78rem; }
  body.touch-mode .inv-search-ic { left: 10px; font-size: .8rem; }
  body.touch-mode .inv-tabs .char-tab { padding: 3px 10px; font-size: .72rem; }
  body.touch-mode .inv-scroll { padding: 6px 14px 8px; }

  /* tighter cells — three rows of items in the space that held one and a half */
  body.touch-mode .inv-scroll .grid { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); gap: 8px; }
  body.touch-mode .inv-scroll .cell { padding: 5px 3px; border-radius: 12px; }
  body.touch-mode .inv-scroll .cell .emoji,
  body.touch-mode .inv-scroll .cell .weap-fb { font-size: 1.45rem; }
  body.touch-mode .inv-scroll .cell .weap-wrap { width: 34px; height: 34px; }
  body.touch-mode .inv-scroll .cell .cname { font-size: .6rem; margin-top: 1px; }
  body.touch-mode .inv-scroll .cell .qty { font-size: .62rem; padding: 0 4px; }

  /* the footer, sideways: the three actions stop being full-width slabs and
     sit at the right end of the same line as the item's text, so they cover
     no part of the grid. The card is capped and its text scrolls inside —
     a decked-out weapon runs to five lines and must not shove the grid off
     the screen again. */
  body.touch-mode .inv-detail {
    flex-direction: row; align-items: center; gap: 10px;
    margin: 4px 14px 8px; padding: 6px 10px; max-height: 96px;
  }
  body.touch-mode .inv-detail .invd-main {
    flex: 1 1 auto; min-width: 0; max-height: 82px;
    align-items: flex-start; overflow-y: auto; overscroll-behavior: contain;
  }
  body.touch-mode .inv-detail .r-up { margin-top: 3px; padding: 2px 7px; }
  body.touch-mode .inv-detail .invd-ic { width: 34px; height: 34px; font-size: 1.4rem; }
  body.touch-mode .inv-detail .invd-ic .weap-wrap { width: 30px; height: 30px; }
  body.touch-mode .invd-actions { flex: 0 0 auto; gap: 6px; }
  body.touch-mode .invd-btn { flex: 0 0 auto; padding: 9px 15px; font-size: .76rem; border-radius: 11px; }

  /* NPC dialogue gets the same wide treatment */
  body.touch-mode .dialogue-box { max-width: 720px; }

  /* toasts drop just under the system-log ticker instead of mid-screen.
     Landscape phones are short (≤520px tall), so a full-size pill floating in
     the upper-centre blankets the play field — shrink the pill and hug it to
     the top edge so it reads as a thin banner, not a screen-cover. */
  body.touch-mode #toast-area { top: calc(6px + env(safe-area-inset-top)); max-width: 60vw; }
  body.touch-mode .toast {
    padding: 5px 14px; font-size: .82rem; border-width: 2px;
    box-shadow: inset 0 0 0 1.5px #fff8ea, 0 3px 8px rgba(120,85,45,.22);
  }

  /* Quest tracker stays frameless in landscape too (matches portrait) —
     widen it a touch and let goal text wrap to two lines instead of
     chopping Thai words mid-way ("ขวาน 0..."). */
  body.touch-mode #quest-tracker:not(.qt-minimized) { width: 236px; }
  body.touch-mode #quest-tracker .qt-goal-text {
    white-space: normal;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  }

  /* ---- Hotbar: use the strip between the thumb clusters ----
     Wider bar than portrait, but keep the slots modest — full-width
     10vw slots read as oversized on phone screens. */
  body.touch-mode #bottom-bar {
    left: calc(130px + env(safe-area-inset-left));
    right: calc(130px + env(safe-area-inset-right));
  }
  body.touch-mode .hb-slot {
    width: clamp(44px, 6.5vw, 58px);
    height: clamp(46px, 6vw, 54px);
  }
}

/* ---- Backpack on an SE-class landscape screen (320px tall, ~568 wide) ----
   Every row of chrome here costs a row of items, so: the search / capacity /
   upgrade line is forced onto ONE line (it wrapped into two and cost ~37px),
   item names are clipped to one line (a two-line name grew every cell in the
   row by 15px), and the sideways footer is capped tighter. */
@media (orientation: landscape) and (max-height: 520px) and (max-width: 719px) {
  body.touch-mode .inv-hrow { flex-wrap: nowrap; gap: 6px; }
  body.touch-mode .inv-hrow .inv-search-wrap { flex: 1 1 90px; min-width: 80px; }
  body.touch-mode .inv-hrow .inv-cap { flex: 1 1 100px; min-width: 90px; }
  body.touch-mode .inv-hrow .bag-up-costs { display: none; }  /* the meter already says the bag is full */
  body.touch-mode .inv-scroll .cell .cname { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  body.touch-mode .inv-detail { max-height: 80px; }
  body.touch-mode .inv-detail .invd-main { max-height: 66px; }
}

/* ---- Backpack, two-pane: wide landscape phones (iPhone 13/15/16-class and up)
   Once the panel is wide enough to spare ~240px, the detail card leaves the
   bottom entirely and becomes a right-hand COLUMN. Two things fall out of it:
   the item grid keeps a constant height no matter which item is selected (a
   weapon card no longer eats two rows of items), and the action buttons are
   permanently off the grid. Below this width the sideways footer above is
   still the better deal — an SE-class screen only fits 3 columns of items
   once a fixed column is carved off the side.
   Everything here is layout-only; the markup is the same in both. */
@media (orientation: landscape) and (max-height: 520px) and (min-width: 720px) {
  body.touch-mode #panel-body.pb-inv {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 240px;
    grid-template-rows: auto auto minmax(0, 1fr);
  }
  body.touch-mode .pb-inv .inv-head,
  body.touch-mode .pb-inv .inv-top { grid-column: 1 / -1; }
  body.touch-mode .pb-inv .inv-scroll { grid-column: 1; grid-row: 3; }

  /* the detail card: full column height, its text scrolling inside, the
     actions pinned to the bottom of the column */
  body.touch-mode .pb-inv .inv-detail {
    grid-column: 2; grid-row: 3; min-height: 0; max-height: none;
    flex-direction: column; align-items: stretch;
    margin: 4px 12px 10px 2px; padding: 8px 10px; gap: 8px;
  }
  body.touch-mode .pb-inv .inv-detail .invd-main {
    flex: 1 1 auto; min-height: 0; max-height: none;
    overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
  }
  /* the column is narrow but tall — let the lines that were clipped to one
     line in the footer layout wrap instead */
  body.touch-mode .pb-inv .invd-top { flex-wrap: wrap; gap: 2px 8px; }
  body.touch-mode .pb-inv .invd-name,
  body.touch-mode .pb-inv .invd-src { white-space: normal; }
  body.touch-mode .pb-inv .invd-actions { flex: 0 0 auto; }
  body.touch-mode .pb-inv .invd-btn { flex: 1 1 0; padding: 9px 6px; }
}

/* ---------------- portrait lock (landscape-only on touch) ----------------
   NOT a hint — the game is never playable in portrait. Full-bleed, opaque,
   above every panel/modal/toast, and with NO dismiss affordance: rotating the
   phone is the only exit. It's driven by CSS alone (no `hidden` class to
   toggle) so it also covers the title screen, the character creator and any
   panel that happens to be open when the phone turns. iOS Safari can't lock
   orientation from JS, so the gate has to be a wall the player sees.
   Game.paused reads UI.portraitLocked, so the world is frozen behind it. */
#portrait-block {
  position: fixed; inset: 0; z-index: 100000; display: none;
  flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; padding: 24px; text-align: center;
  background: linear-gradient(180deg, #2b1d0e, #12100c);
  color: #fbeccb; overscroll-behavior: none; touch-action: none;
  user-select: none; -webkit-user-select: none;
}
#portrait-block .pb-ic {
  width: 88px; height: 88px; color: #e8c98a;
  animation: pbTurn 2.4s ease-in-out infinite;
}
#portrait-block .pb-title { font-size: 1.15rem; font-weight: 800; letter-spacing: .3px; }
#portrait-block .pb-text { font-size: .86rem; font-weight: 600; opacity: .8; max-width: 20rem; line-height: 1.6; }
@keyframes pbTurn {
  0%, 55%, 100% { transform: rotate(0deg); }
  75%, 92%      { transform: rotate(-90deg); }
}
@media (orientation: portrait) {
  body.touch-mode #portrait-block { display: flex; }
}

/* "tap for fullscreen" pill — same look, landscape browser-tab play only
   (JS also checks standalone/fullscreen state before un-hiding it) */
#fs-hint {
  position: fixed; left: 50%; top: 38%; transform: translateX(-50%);
  z-index: 80; display: none; align-items: center; gap: 10px;
  max-width: min(86vw, 340px); padding: 10px 18px; border-radius: 999px;
  background: linear-gradient(180deg, #fffaf0, #fbeccb); color: var(--ink);
  font-size: .82rem; font-weight: 800; border: 2.5px solid var(--wood);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 6px 16px rgba(120,85,45,.3);
  cursor: pointer; animation: toastIn .3s ease;
}
#fs-hint .fh-ic { width: 1.4rem; height: 1.4rem; flex-shrink: 0; color: var(--wood-dark); }
@media (orientation: landscape) {
  body.touch-mode #fs-hint:not(.hidden) { display: flex; }
}

/* "Add to Home Screen" step-by-step guide — small centered modal shown once
   per session on mobile (touch) players in a plain browser tab (not yet
   standalone/PWA); closed only by the player tapping the × or backdrop.
   Reuses the same wood/parchment look as .panel. */
#home-guide {
  position: fixed; inset: 0; z-index: 90; display: none;
  align-items: center; justify-content: center; padding: 16px;
  background: rgba(94,67,31,.5); backdrop-filter: blur(3px);
}
#home-guide:not(.hidden) { display: flex; }
.home-guide-card {
  position: relative; max-width: 320px; width: 100%; padding: 22px 24px 20px;
  background: linear-gradient(180deg, #fffaf0, #fbeccb); color: var(--ink);
  border-radius: 22px; border: 3px solid var(--wood);
  box-shadow: inset 0 0 0 2px #fff8ea, 0 18px 44px rgba(120,85,45,.35);
  animation: pop .25s cubic-bezier(.2,1.2,.4,1);
}
.home-guide-close {
  position: absolute; top: 10px; right: 10px; width: 30px; height: 30px;
  border-radius: 50%; border: none; background: #f5e0b8; color: var(--wood-dark);
  font-size: 1.2rem; line-height: 1; cursor: pointer;
}
.home-guide-close:hover { background: #ecd6ac; }
.hg-title { margin: 0 28px 10px 0; color: var(--gold-hi); font-size: 1.05rem; }
.hg-warn { margin: 0 0 12px; line-height: 1.5; font-size: .9rem; font-weight: 700; color: #a3401b; }
.hg-steps { margin: 0; line-height: 1.6; font-size: .9rem; white-space: pre-line; }

/* ============================================================
   Account / Login / Leaderboard  (cloud layer)
   ============================================================ */

/* Title-screen "players have played" badge — a live cloud count */
#title-players {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 0; padding: 6px 16px 6px 12px;
  background: rgba(255,255,255,.18);
  border: 1px solid rgba(255,255,255,.32);
  border-radius: 999px; backdrop-filter: blur(4px);
  box-shadow: 0 3px 12px rgba(80,110,90,.2), inset 0 1px 0 rgba(255,255,255,.35);
  animation: tp-pop .5s ease both;
}
#title-players .tp-dot {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: #5fd08a; box-shadow: 0 0 0 0 rgba(95,208,138,.6);
  animation: tp-pulse 2s ease-out infinite;
}
#title-players .tp-num {
  font-weight: 900; font-size: 1.18rem; color: #fff;
  letter-spacing: .3px; text-shadow: 0 2px 4px rgba(80,110,90,.45);
}
#title-players .tp-label {
  font-size: .82rem; color: #fff8ea; opacity: .92;
  text-shadow: 0 1px 3px rgba(80,110,90,.4);
}
@keyframes tp-pop {
  from { opacity: 0; transform: translateY(6px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
@keyframes tp-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(95,208,138,.55); }
  70%  { box-shadow: 0 0 0 7px rgba(95,208,138,0); }
  100% { box-shadow: 0 0 0 0 rgba(95,208,138,0); }
}

/* Title-screen account strip — who's signed in + sign-out / set-name.
   Sits on the DARK dimmed title art, so it uses the same light-cream +
   shadow treatment as .subtitle/.title-hint — the panel browns (--ink /
   --gold-hi) are unreadable here. */
#title-account {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  flex-wrap: wrap; margin: 0; font-size: .88rem;
  color: #fff8ea; text-shadow: 0 1px 3px rgba(80,110,90,.4);
}
#title-account .ta-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: #cbb68c; box-shadow: 0 0 0 2px rgba(255,248,234,.25);
}
#title-account .ta-dot.ta-on { background: var(--good); }
#title-account .ta-pre  { opacity: .9; }
#title-account .ta-name { font-weight: 800; color: #fff8ea; }
#title-account .ta-link {
  background: none; border: none; cursor: pointer; padding: 2px 6px;
  font-family: var(--font); font-weight: 800; font-size: .9rem;
  color: #ffce5c; text-shadow: 0 1px 3px rgba(80,110,90,.5);
  text-decoration: underline; text-underline-offset: 2px;
}
#title-account .ta-link:hover { color: #fff8ea; }

/* Nickname / login box (rendered into the shared panel overlay) */
.login-box { text-align: center; padding: 6px 4px 2px; }
.login-box h2 { margin: 0 0 6px; color: var(--gold-hi); }
.login-box .login-sub { margin: 0 auto 14px; max-width: 320px; color: var(--ink-soft); }
.nick-input {
  width: 100%; max-width: 320px; box-sizing: border-box;
  padding: 12px 14px; font-family: var(--font); font-size: 1.1rem; font-weight: 700;
  text-align: center; color: var(--ink);
  background: linear-gradient(#fffaf0, #fbeccf);
  border: 2px solid var(--wood); border-radius: 14px; outline: none;
  box-shadow: inset 0 2px 4px var(--shadow);
}
.nick-input:focus { border-color: var(--gold); box-shadow: 0 0 0 3px rgba(234,165,62,.28); }
.login-actions { margin: 16px 0 6px; display: flex; justify-content: center; }
.login-box .login-note { margin: 8px auto 0; max-width: 320px; font-size: .82rem; color: var(--ink-soft); opacity: .85; }

/* Character creator */
.cc-box { max-width: 460px; margin: 0 auto; }
.cc-main { display: flex; gap: 16px; align-items: flex-start; justify-content: center; margin: 4px 0 2px; }
.cc-preview {
  flex: 0 0 auto; border-radius: 16px; padding: 6px;
  background: linear-gradient(#fffaf0, #f3e3c4); border: 2px solid var(--wood);
  box-shadow: inset 0 2px 6px var(--shadow);
}
.cc-preview canvas { display: block; image-rendering: pixelated; }
.cc-roll-btn {
  display: block; width: 100%; margin: 8px 0 0; font-family: var(--font); font-weight: 700; font-size: .82rem; cursor: pointer;
  padding: 5px 10px; color: var(--ink); border-radius: 11px;
  background: rgba(255,248,234,.85); border: 2px solid var(--wood);
  box-shadow: 0 3px 0 rgba(138,95,51,.2); transition: transform .08s, box-shadow .08s;
}
.cc-roll-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(138,95,51,.2); }
.cc-opts { flex: 1 1 auto; min-width: 0; text-align: left; }
.cc-label { display: block; font-weight: 800; color: var(--wood-dark); font-size: .82rem; margin: 8px 2px 5px; }
.cc-opts .cc-label:first-child { margin-top: 0; }
.cc-row { display: flex; flex-wrap: wrap; gap: 6px; }
.cc-chip {
  font-family: var(--font); font-weight: 700; font-size: .85rem; cursor: pointer;
  padding: 6px 12px; color: var(--ink); border-radius: 11px;
  background: rgba(255,248,234,.85); border: 2px solid var(--wood);
  box-shadow: 0 3px 0 rgba(138,95,51,.2); transition: transform .08s, box-shadow .08s;
}
.cc-chip:active { transform: translateY(2px); box-shadow: 0 1px 0 rgba(138,95,51,.2); }
.cc-chip.sel { border-color: var(--gold); box-shadow: 0 0 0 3px rgba(234,165,62,.32); }
.cc-sw .cc-chip {
  width: 34px; height: 34px; padding: 0; border-radius: 50%;
  box-shadow: 0 2px 0 rgba(0,0,0,.2);
}
.cc-sw .cc-chip.sel { box-shadow: 0 0 0 3px var(--gold-hi), 0 0 0 5px rgba(234,165,62,.4); }

.fb-box { max-width: 420px; margin: 0 auto; }
.fb-label { display: block; text-align: left; font-weight: 800; color: var(--wood-dark); font-size: .9rem; margin: 12px 2px 5px; }
.fb-area {
  width: 100%; box-sizing: border-box; resize: vertical;
  padding: 10px 12px; font-family: var(--font); font-size: 1rem; line-height: 1.4; color: var(--ink);
  background: linear-gradient(#fffaf0, #fbeccf);
  border: 2px solid var(--wood); border-radius: 14px; outline: none;
  box-shadow: inset 0 2px 4px var(--shadow);
}
.fb-area:focus { border-color: var(--gold); box-shadow: 0 0 0 3px rgba(234,165,62,.28); }

.fb-stars { display: flex; justify-content: center; gap: 10px; margin: 10px 0 4px; }
.fb-star {
  font-size: 2.2rem; cursor: pointer; line-height: 1;
  color: #999; transition: color .12s, transform .1s;
  user-select: none;
}
.fb-star.active { color: #f5a000; transform: scale(1.15); }
.fb-star:hover { color: #f5a000; transform: scale(1.2); }

/* Leaderboard — board tabs, period switch, list */
.lb-tabs {
  display: flex; gap: 6px; margin-bottom: 8px;
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.lb-tabs::-webkit-scrollbar { display: none; }
.lb-tab, .lb-per {
  flex: 0 0 auto; padding: 7px 12px; border-radius: 10px; cursor: pointer;
  font-family: inherit; font-weight: 800; font-size: .82rem; white-space: nowrap;
  color: var(--ink-soft); background: linear-gradient(var(--parch), var(--parch-2));
  border: 1.5px solid var(--gold-lo);
}
.lb-tab.on, .lb-per.on {
  color: var(--ink); border-color: var(--gold);
  background: linear-gradient(#fff3d6, #ffe7b0); box-shadow: 0 2px 0 var(--shadow);
}
.lb-periods { display: flex; gap: 6px; margin-bottom: 6px; }
.lb-desc { margin: 0 0 10px; }

/* Podium — the top three stand on their plinths in the gear they earned.
   DOM order is already 2·1·3 (nothing here reorders) and each column is
   [figure][plinth][name][score]: bottom-aligning the columns lines the NAMES up
   while the taller gold plinth lifts #1's feet above the other two. */
.lb-podium {
  display: flex; align-items: flex-end; justify-content: center;
  gap: 6px; margin-bottom: 14px;
}
.lb-pod { flex: 1 1 0; min-width: 0; text-align: center; }
.lb-pod .lb-fig {
  display: block; width: 78px; max-width: 100%; height: auto; margin: 0 auto -1px;
  image-rendering: pixelated;
}
.lb-pod-1 .lb-fig { width: 96px; }
/* A plinth whose sprite sheets are still decoding shimmers instead of sitting
   blank — opened off the title screen, the LPC sheets only START loading then,
   so "nothing there yet" has to read as loading, never as broken. */
.lb-pod-wait .lb-fig {
  border-radius: 8px;
  background: linear-gradient(100deg,
    rgba(120, 92, 56, .07) 30%, rgba(120, 92, 56, .17) 50%, rgba(120, 92, 56, .07) 70%);
  background-size: 220% 100%;
  animation: lb-fig-wait 1.15s linear infinite;
}
@keyframes lb-fig-wait {
  from { background-position: 180% 0; }
  to   { background-position: -80% 0; }
}
.lb-pod-name {
  margin-top: 5px; font-weight: 800; font-size: .78rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-pod-1 .lb-pod-name { font-size: .86rem; }
.lb-pod-val {
  font-weight: 800; font-size: .72rem; color: var(--gold-hi);
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lb-pod-step {
  display: flex; align-items: center; justify-content: center;
  border-radius: 6px 6px 3px 3px; border: 1.5px solid var(--gold-lo);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .5), 0 2px 0 var(--shadow);
  font-weight: 800; font-size: 1.1rem; color: #fff;
  text-shadow: 0 1px 1px rgba(0, 0, 0, .35);
}
.lb-pod-1 .lb-pod-step { height: 54px; background: linear-gradient(#f5cd63, #d59f1c); font-size: 1.35rem; }
.lb-pod-2 .lb-pod-step { height: 38px; background: linear-gradient(#dfe5ea, #a8b2bb); }
.lb-pod-3 .lb-pod-step { height: 28px; background: linear-gradient(#e5b78b, #bb7c47); }
/* your own plinth gets the same gold ring the list row uses */
.lb-pod.lb-me .lb-pod-step { border-color: var(--gold); }
.lb-pod.lb-me .lb-pod-name { color: var(--gold-hi); }

.lb-list { display: flex; flex-direction: column; gap: 6px; }
.lb-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-radius: 12px;
  background: linear-gradient(var(--parch), var(--parch-2));
  border: 1.5px solid var(--gold-lo);
}
.lb-row .lb-rank {
  flex: 0 0 30px; text-align: center; font-weight: 800; color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}
/* no medal colours here — ranks 1-3 are on the podium above, so the list
   always starts at 4 and a positional rule would gild the wrong rows. */
.lb-row .lb-name { flex: 1 1 auto; font-weight: 800; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-row .lb-days {
  flex: 0 0 auto; font-weight: 800; color: var(--gold-hi);
  font-variant-numeric: tabular-nums; text-align: right; line-height: 1.15;
}
.lb-row .lb-sub { display: block; font-size: .72rem; font-weight: 700; color: var(--ink-soft); }
.lb-row.lb-me {
  border-color: var(--gold); background: linear-gradient(#fff3d6, #ffe7b0);
  box-shadow: 0 2px 0 var(--shadow);
}
.lb-foot { margin-top: 12px; }

/* numeric "1·2·3" glyph used in place of an emoji in the menu list */
.r-icon.r-icon-rank { font-size: .8rem; font-weight: 800; letter-spacing: -.5px; color: var(--gold-hi); }

/* home icon in menu */
.r-icon.r-icon-home { font-size: 1.3rem; line-height: 1; color: var(--ink); }

/* svg icon rows in menu (fullscreen, exit-to-menu) */
.recipe .r-icon-svg { width: 28px; height: 28px; flex: 0 0 28px; color: var(--ink-soft); }

/* menu section dividers */
.menu-sec {
  font-size: .68rem; font-weight: 800; color: var(--gold-hi);
  letter-spacing: .7px; text-transform: uppercase;
  display: flex; align-items: center; gap: 8px;
  margin: 10px 0 7px;
}
.menu-sec:first-child { margin-top: 2px; }
.menu-sec::before, .menu-sec::after { content: ""; flex: 1; height: 1.5px; background: var(--gold-lo); }

/* compact audio card inside menu */
.menu-audio-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 9px; }
.menu-audio-row { display: flex; align-items: center; gap: 8px; margin-top: 5px; }
/* muted → sliders dim but stay live: dragging one auto-unmutes (_onPanelInput) */
.menu-audio-row.muted { opacity: .45; }
.menu-audio-row .r-desc { flex: 0 0 auto; min-width: 54px; }
.menu-audio-row .vol-slider { margin-top: 0; }

/* ---- timed-effect chips (meal buffs / poison / rested) under the stat bars ---- */
#buff-row { display: flex; gap: 5px; margin-top: 4px; flex-wrap: wrap; max-width: 190px; }
#buff-row.hidden { display: none; }
.buff-chip {
  font-size: .62rem; font-weight: 800; padding: 2px 8px; border-radius: 999px;
  background: linear-gradient(180deg, #fffaf0, #f1ddb4);
  border: 1.5px solid var(--gold-lo); color: var(--gold-hi);
  box-shadow: 0 2px 5px rgba(120,85,45,.2); white-space: nowrap;
}
.buff-chip.bad {
  border-color: #b06a9a; color: #8a3a7a;
  background: linear-gradient(180deg, #f9e8f4, #ecd0e4);
}

/* ---------------- mobile compositing: kill backdrop-filter ----------------
   backdrop-filter: blur() is re-evaluated every frame against whatever is
   behind it. Over the game canvas (which repaints every frame) iOS Safari pays
   that blur PER FRAME for every blurred element on screen — the touch controls
   (side-nav, jump button) PLUS the system-log toast that pops for 5s on every
   gather/notify. Stacked, the frame rate collapses (the reported FPS 7 at
   work 6ms = pure compositing, zero JS cost; clears when the toast hides).
   Drop them all on touch and lift the now-unblurred backgrounds so everything
   stays readable. */
body.touch-mode #system-log,
body.touch-mode #panel-overlay,
body.touch-mode #storage-modal,
body.touch-mode #quest-celebrate,
body.touch-mode #dash-btn,
body.touch-mode #jump-btn,
body.touch-mode #playdead-btn,
body.touch-mode #guard-btn,
body.touch-mode #side-nav .nav-btn {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}
body.touch-mode #system-log {
  background: linear-gradient(180deg, rgba(255,250,240,.94), rgba(241,221,180,.94));
}
body.touch-mode #dash-btn      { background: rgba(31,22,12,.55); }
body.touch-mode #jump-btn      { background: rgba(31,22,12,.55); }
body.touch-mode #playdead-btn  { background: rgba(31,22,12,.55); }
body.touch-mode #guard-btn     { background: rgba(31,22,12,.55); }
body.touch-mode #panel-overlay { background: rgba(94,67,31,.52); }
body.touch-mode #storage-modal { background: rgba(60,40,20,.8); }
body.touch-mode #quest-celebrate { background: rgba(94,67,31,.7); }

/* ---------------- desktop compositing: kill backdrop-filter too ----------------
   The mobile rule above was never extended to desktop, but the trap is identical
   on a Retina display: every blurred HUD panel over the live game canvas (which
   repaints every frame) is re-blurred PER FRAME by the compositor. The always-on
   #chat-box is the worst — the longer you play the more it (and the toasts that
   pop on every gather) stay on screen, so FPS collapses over a session (measured:
   beach night 180 FPS fresh -> 27 FPS after play) while `work` stays ~2ms: pure
   compositing, zero JS, exactly the symptom the mobile comment documents. The
   blur is decorative glass; drop it everywhere and lift the now-unblurred
   backgrounds so panels stay readable. */
#system-log,
#panel-overlay,
#storage-modal,
#quest-celebrate,
#chat-box {
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}
#chat-box {
  background: linear-gradient(180deg, rgba(255,250,240,.9), rgba(241,221,180,.9));
}

/* ============================================================
   Forge economy — HUD coin chip (in the profile row), enchant
   modal, login calendar. Matches the parchment/wood game chrome.
   ============================================================ */
/* scrap-coin balance — a compact chip sitting beside the level chip in the
   top-left profile row */
#coin-chip {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  margin: 0; pointer-events: auto;
  background: linear-gradient(180deg, #fffaf0, #f6e1b9);
  border: 2.5px solid var(--wood); border-radius: 999px; padding: 2px 12px 2px 5px;
  box-shadow: inset 0 0 0 1.5px #fff8ea, 0 2px 5px rgba(120,85,45,.18);
  font-weight: 800; color: var(--gold-hi); font-size: 1rem; line-height: 1.5;
}
#coin-chip .coin-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.3rem; height: 1.3rem; border-radius: 50%;
  background: radial-gradient(circle at 38% 32%, #ffe89a, #e0a52e);
  color: #7a5410; font-weight: 900; font-size: .92rem;
  box-shadow: inset 0 0 0 1.5px #fff3c8, 0 1px 2px rgba(120,85,45,.4);
}
#coin-count { color: var(--ink); }

/* the chip bounces as each flying coin lands in the purse */
#coin-chip.coin-pop { animation: coinChipPop .45s cubic-bezier(.2, 1.7, .4, 1); }
#coin-chip.coin-pop .coin-ico { animation: coinIcoSpin .45s ease-out; }
@keyframes coinChipPop {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.22); box-shadow: inset 0 0 0 1.5px #fff8ea, 0 2px 10px rgba(230, 170, 40, .55); }
  100% { transform: scale(1); }
}
@keyframes coinIcoSpin {
  0%   { transform: scaleX(1); }
  50%  { transform: scaleX(-1); filter: brightness(1.5); }
  100% { transform: scaleX(1); }
}

/* a coin picked up in the world, arcing up to the purse (positioned +
   animated from JS via WAAPI — transform/opacity only, so it composites) */
.coin-fly {
  position: fixed; z-index: 60; pointer-events: none;
  width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 38% 32%, #ffe89a, #e0a52e);
  color: #7a5410; font-weight: 900; font-size: .8rem;
  box-shadow: inset 0 0 0 1.5px #fff3c8, 0 1px 4px rgba(120, 85, 45, .5);
  will-change: transform, opacity;
}

/* enchant level tag (+N) */
.ench-tag {
  display: inline-block; padding: 0 6px; border-radius: 999px;
  background: linear-gradient(180deg, #8be0ff, #3aa6e0); color: #06324a;
  font-weight: 900; font-size: .76rem; vertical-align: middle;
  box-shadow: inset 0 0 0 1.5px #d8f4ff, 0 1px 3px rgba(20,90,130,.4);
}
.recipe.enchanted { box-shadow: inset 0 0 0 1.5px rgba(58,166,224,.5); }
.r-btns { display: flex; flex-direction: column; gap: 5px; }
.forge-btn { background: linear-gradient(180deg, #bfe9ff, #5cb8e8) !important; color: #06324a !important; }

/* ---- forge modal ---- */
#forge-modal {
  position: fixed; inset: 0; z-index: 100; pointer-events: all;
  display: flex; align-items: center; justify-content: center;
  background: rgba(94,67,31,.42); animation: fadeInBg .25s ease;
}
.fm-card {
  position: relative; background: linear-gradient(175deg, #fffcf0, #fae8c0);
  border: 3px solid var(--wood); border-radius: 22px; padding: 18px 20px 20px;
  width: 90vw; max-width: 340px; text-align: center;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 24px 56px rgba(120,85,45,.48);
  animation: celebPop .34s cubic-bezier(.2,1.4,.4,1) forwards;
}
.fm-x { position: absolute; top: 8px; right: 10px; border: 0; background: none; font-size: 1.6rem; line-height: 1; color: var(--wood-dark); cursor: pointer; }
.fm-title { font-weight: 900; color: var(--gold-hi); font-size: 1.2rem; margin-bottom: 12px; }
.fm-weap { display: flex; align-items: center; gap: 12px; text-align: left; background: rgba(255,255,255,.5); border: 2px solid var(--gold-lo); border-radius: 14px; padding: 10px 12px; }
.fm-weap .fm-ic { font-size: 2rem; }
.fm-name { font-weight: 800; color: var(--ink); }
.fm-dmg { font-size: .92rem; color: var(--wood-dark); margin-top: 2px; }
.fm-arrow { color: var(--gold-hi); font-weight: 700; }
.fm-stats { display: flex; gap: 10px; margin: 12px 0 8px; }
.fm-stat { flex: 1; background: rgba(255,255,255,.55); border: 2px solid var(--gold-lo); border-radius: 12px; padding: 8px 6px; }
.fm-stat span { display: block; font-size: .68rem; color: var(--wood-mid); font-weight: 700; }
.fm-stat b { display: block; font-size: 1.05rem; margin-top: 2px; color: var(--ink); }
.fm-stat b.good { color: #2f9e57; } .fm-stat b.warn { color: #d68a1e; } .fm-stat b.danger { color: #d6442e; }
.fm-risk { font-size: .8rem; font-weight: 700; border-radius: 10px; padding: 6px 8px; margin-bottom: 8px; }
.fm-risk.safe { background: rgba(64,180,110,.16); color: #1f7a44; }
.fm-risk.warn { background: rgba(230,160,40,.18); color: #9c6a10; }
.fm-risk.danger { background: rgba(220,70,46,.16); color: #b23320; }
.fm-seal { display: flex; align-items: center; justify-content: center; gap: 5px; font-size: .82rem; color: var(--wood-dark); margin-bottom: 10px; cursor: pointer; }
.fm-seal.off { opacity: .45; }
.fm-bal { font-size: .92rem; color: var(--wood-dark); margin: 6px 0 12px; }
.fm-bal b { color: var(--ink); }
.fm-go {
  width: 100%; border: 2.5px solid var(--wood); border-radius: 14px; padding: 11px;
  background: linear-gradient(180deg, #ffe19a, var(--gold)); color: #6e4310;
  font-weight: 800; font-size: 1rem; cursor: pointer; box-shadow: inset 0 0 0 2px #fff8ea;
}
.fm-go:disabled { opacity: .5; cursor: default; }
.fm-go:active:not(:disabled) { transform: translateY(1px); }
.fm-banner { font-weight: 800; border-radius: 12px; padding: 9px; margin: 12px 0 2px; }
.fm-banner.ok { background: rgba(64,180,110,.2); color: #1f7a44; }
.fm-banner.meh { background: rgba(150,150,150,.16); color: #6a5a40; }
.fm-banner.bad { background: rgba(220,70,46,.18); color: #b23320; }
.fm-card.fm-ok { animation: fmFlashOk .5s ease; }
.fm-card.fm-shake { animation: fmShake .4s ease; }
@keyframes fmFlashOk { 0% { box-shadow: inset 0 0 0 2px #fff8ea, 0 0 0 0 rgba(64,200,120,.7), 0 24px 56px rgba(120,85,45,.48); } 100% { box-shadow: inset 0 0 0 2px #fff8ea, 0 0 0 14px rgba(64,200,120,0), 0 24px 56px rgba(120,85,45,.48); } }
@keyframes fmShake { 10%,90% { transform: translateX(-2px); } 30%,70% { transform: translateX(4px); } 50% { transform: translateX(-5px); } }

/* ---- login calendar ---- */
#login-cal {
  position: fixed; inset: 0; z-index: 100; pointer-events: all;
  display: flex; align-items: center; justify-content: center;
  background: rgba(94,67,31,.42); animation: fadeInBg .25s ease;
}
.lc-card {
  background: linear-gradient(175deg, #fffcf0, #fae8c0);
  border: 3px solid var(--wood); border-radius: 22px; padding: 18px 18px 20px;
  width: 92vw; max-width: 380px; text-align: center;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 24px 56px rgba(120,85,45,.48);
  animation: celebPop .34s cubic-bezier(.2,1.4,.4,1) forwards;
}
.lc-title { font-weight: 900; color: var(--gold-hi); font-size: 1.25rem; }
.lc-sub { font-size: .82rem; color: var(--wood-mid); font-weight: 700; margin-bottom: 12px; }
.lc-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; margin-bottom: 14px; }
.lc-cell { position: relative; border: 2px solid var(--gold-lo); border-radius: 12px; padding: 7px 3px; background: rgba(255,255,255,.5); }
.lc-cell .lc-day { font-size: .66rem; font-weight: 800; color: var(--wood-mid); }
.lc-cell .lc-rew { font-size: .72rem; font-weight: 700; color: var(--ink); margin-top: 3px; line-height: 1.35; }
.lc-cell.today { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(234,165,62,.4); background: linear-gradient(180deg, #fff6df, #ffedc2); }
.lc-cell.done { opacity: .6; }
.lc-cell .lc-check { position: absolute; top: 3px; right: 5px; color: #2f9e57; font-weight: 900; font-size: .85rem; }

/* ---- "ระหว่างที่ไม่อยู่" — the returning-player card (UI.showAwayReport) ----
   Same parchment card family as the login calendar so a return reads as part of
   the daily loop, not a system dialog. Deliberately small: three rows max. */
#away-report {
  position: fixed; inset: 0; z-index: 100; pointer-events: all;
  display: flex; align-items: center; justify-content: center;
  background: rgba(94,67,31,.42); animation: fadeInBg .25s ease;
}
.awr-card {
  position: relative;
  background: linear-gradient(175deg, #fffcf0, #fae8c0);
  border: 3px solid var(--wood); border-radius: 22px; padding: 18px 18px 20px;
  width: 92vw; max-width: 360px; text-align: center;
  box-shadow: inset 0 0 0 2px #fff8ea, 0 24px 56px rgba(120,85,45,.48);
  animation: celebPop .34s cubic-bezier(.2,1.4,.4,1) forwards;
}
.awr-x {
  position: absolute; top: 6px; right: 8px; width: 30px; height: 30px;
  border: none; background: none; color: var(--wood-mid);
  font-size: 1.4rem; font-weight: 900; line-height: 1; cursor: pointer;
}
.awr-title { font-weight: 900; color: var(--gold-hi); font-size: 1.22rem; }
.awr-sub { font-size: .82rem; color: var(--wood-mid); font-weight: 700; margin-bottom: 12px; }
.awr-rows { display: flex; flex-direction: column; gap: 7px; margin-bottom: 12px; }
.awr-row {
  display: flex; align-items: center; gap: 9px; text-align: left;
  border: 2px solid var(--gold-lo); border-radius: 12px; padding: 8px 11px;
  background: rgba(255,255,255,.5);
  font-size: .86rem; font-weight: 700; color: var(--ink);
}
/* holds a baked item PNG (UI._ic → .ic-sm), never an emoji */
.awr-ic { flex: none; display: flex; align-items: center; width: 22px; }
.awr-ic .ic-sm { width: 22px; height: 22px; }
.awr-streak {
  font-size: .8rem; font-weight: 800; color: #6e4310; margin-bottom: 12px;
  background: linear-gradient(180deg, #fff6df, #ffedc2);
  border: 2px solid var(--gold); border-radius: 12px; padding: 7px;
}

/* ---- Continue-button badge on the title screen (UI._titleSaveBadge) ----
   Sits on the sky-blue title art, so it takes the same light ink + soft shadow as
   .title-hint / #title-account rather than the panel browns. */
#title-save-badge {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  flex-wrap: wrap; font-size: .82rem; font-weight: 700;
  color: #fff8ea; text-shadow: 0 1px 3px rgba(80,110,90,.45);
  margin-top: -2px;
}
#title-save-badge .tsb-hot { color: #ffe6a8; font-weight: 800; }
#title-save-badge .tsb-risk { color: #ffcf9a; font-weight: 800; }
#title-save-badge .tsb-sep { opacity: .5; }

/* generic Scrap Coin badge — the gold ₡ chip, used everywhere a coin appears
   (daily rewards, crafting costs, shop, quest payouts). em-sized so it scales to
   the surrounding text; #coin-chip / .inv-coin-row keep their own fixed sizes. */
.coin-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.25em; height: 1.25em; border-radius: 50%; vertical-align: -0.2em;
  background: radial-gradient(circle at 38% 32%, #ffe89a, #e0a52e);
  color: #7a5410; font-weight: 900; font-size: .8em; line-height: 1;
  box-shadow: inset 0 0 0 1.5px #fff3c8, 0 1px 2px rgba(120,85,45,.4);
}

/* ---------- ปล่องดิ่ง: the live strip shown while a shaft run is on ----------
   Sits under the stat bars, centred, out of the quest tracker's column. Its
   children are only ever written with textContent (see UI.updateDescentHUD) —
   never rebuilt — so it can update every quarter-second without eating taps. */
#descent-strip {
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: center;
  max-width: calc(100vw - 24px); pointer-events: none; z-index: 6;
  font-weight: 800; font-size: .82rem; color: #f3e7cf;
}
#descent-strip.hidden { display: none; }
#descent-strip > span {
  background: rgba(24,18,30,.82); border: 1.5px solid rgba(150,120,90,.55);
  border-radius: 999px; padding: 3px 11px; white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0,0,0,.35);
}
#descent-strip .ds-floor { color: #8fd6ef; border-color: rgba(120,190,225,.6); }
#descent-strip .ds-affix { color: #ffcf7a; border-color: rgba(220,170,90,.6); }
#descent-strip .ds-bag   { color: #ffe9a8; }
#descent-strip .ds-left.clear { color: #9be89b; border-color: rgba(120,210,120,.6); }
/* landscape phones: the strip drops to one tight line so it never crowds the HUD */
@media (orientation: landscape) and (max-height: 520px) {
  #descent-strip { top: 4px; font-size: .72rem; gap: 5px; }
  #descent-strip > span { padding: 2px 8px; }
}

/* ============================================================
   วงล้อสกิลโฟกัส + ปรับปุ่มเอง (touch, LANDSCAPE-first)
   Portrait is no longer developed — every size here is measured
   against a landscape phone (e.g. 844×390).
   ============================================================ */

/* ---------------- the skill wheel's focus mode ----------------
   Holding ACTION is a one-thing-at-a-time moment: every other thumb control
   fades right out (a live dash button beside the ring is only something to hit
   by accident) and the read-only HUD dims back so the ring owns the screen.
   Deliberately opacity-only — a backdrop blur over the whole canvas is exactly
   the composite cost the night-render notes warn about. */
body.sw-focus #dash-btn,
body.sw-focus #jump-btn,
body.sw-focus #playdead-btn,
body.sw-focus #guard-btn,
body.sw-focus #move-btn,
body.sw-focus #cook-btn,
body.sw-focus #touch-controls,
body.sw-focus #side-nav,
body.sw-focus #bottom-bar,
body.sw-focus #prompt-row {
  opacity: 0; pointer-events: none;
  transition: opacity .13s ease-out;
}
/* information, not controls — dimmed rather than gone, so a skill can still be
   chosen against the health bar and the timer */
body.sw-focus #player-hud,
body.sw-focus #top-right,
body.sw-focus #quest-tracker {
  opacity: .22; pointer-events: none;
  transition: opacity .13s ease-out;
}
/* the veil itself: one cheap flat fill, no filter */
#skill-wheel:not(.hidden) {
  background: rgba(8,5,3,.5);
  animation: swVeil .13s ease-out both;
}
@keyframes swVeil { from { background: rgba(8,5,3,0); } to { background: rgba(8,5,3,.5); } }

/* bigger slots — the old 54px circles sat ~48px apart, under one fingertip.
   The size comes from the ring (--sw-s, solved from the spacing the screen
   actually allowed) and the offset rides --tx/--ty, so .on can stack a scale
   on top of it. */
.sw-item {
  width: var(--sw-s, 66px); height: var(--sw-s, 66px);
  transform: translate(-50%, -50%) translate(var(--tx, 0px), var(--ty, 0px));
}
.sw-item .sw-ic { width: 1.85rem; height: 1.85rem; }
.sw-item .sw-lb { font-size: .58rem; max-width: 62px; }
.sw-item.on {
  transform: translate(-50%, -50%) translate(var(--tx, 0px), var(--ty, 0px)) scale(1.16);
}
/* the armed slot is under the thumb — the hub is what you can actually read */
.sw-hub {
  position: absolute; left: 50%; top: calc(-1 * var(--r, 100px) - 40px);
  transform: translate(-50%, -50%);
  padding: 7px 13px; border-radius: 14px; white-space: nowrap;
  font-size: .74rem; font-weight: 800; color: rgba(255,247,232,.82);
  background: rgba(31,22,12,.62); border: 1.5px solid rgba(255,239,210,.22);
  pointer-events: none;
}
.sw-hub.armed { color: #fff8ea; border-color: rgba(255,225,150,.7); background: rgba(76,54,28,.8); }

/* ---------------- ปรับปุ่มเอง — the player's own arrangement ----------------
   TouchLayout writes the position and the box size inline; what CSS still owns
   is everything INSIDE the button, which has to grow with it. --tl-k is the
   scale, --tl-op the opacity, both set on <html> by TouchLayout.apply(). */
/* --tl-i is written on the control itself when it carries its OWN size
   (TouchLayout.apply); it falls back to the global --tl-k, so a button nobody
   resized individually still follows the all-buttons slider. */
body.tl-on .ab-icon  { width: calc(2.3rem * var(--tl-i, var(--tl-k, 1))); height: calc(2.3rem * var(--tl-i, var(--tl-k, 1))); }
body.tl-on .ab-label { font-size: calc(.6rem * var(--tl-i, var(--tl-k, 1))); }
body.tl-on .mb-icon  { width: calc(1.7rem * var(--tl-i, var(--tl-k, 1))); height: calc(1.7rem * var(--tl-i, var(--tl-k, 1))); }
body.tl-on .mb-label { font-size: calc(.55rem * var(--tl-i, var(--tl-k, 1))); }
body.tl-on .db-icon,
body.tl-on .jb-icon  { width: calc(1.55rem * var(--tl-i, var(--tl-k, 1))); height: calc(1.55rem * var(--tl-i, var(--tl-k, 1))); }
body.tl-on .pd-icon,
body.tl-on .gb-icon  { width: calc(1.5rem * var(--tl-i, var(--tl-k, 1))); height: calc(1.5rem * var(--tl-i, var(--tl-k, 1))); }
body.tl-on #joystick-knob {
  width: calc(52px * var(--tl-i, var(--tl-k, 1))); height: calc(52px * var(--tl-i, var(--tl-k, 1)));
  margin: calc(-26px * var(--tl-i, var(--tl-k, 1))) 0 0 calc(-26px * var(--tl-i, var(--tl-k, 1)));
}
/* opacity via filter, NOT the opacity property: #playdead-btn.pd-cool and
   #guard-btn.gb-cool already own `opacity` for their cooldown dim, and a
   custom setting must fade on top of that instead of erasing it */
body.tl-fade #action-btn,
body.tl-fade #dash-btn,
body.tl-fade #jump-btn,
body.tl-fade #playdead-btn,
body.tl-fade #guard-btn,
body.tl-fade #move-btn,
body.tl-fade #joystick { filter: opacity(var(--tl-op, 1)); }

/* ---------------- the layout editor ---------------- */
#layout-edit {
  position: fixed; inset: 0; z-index: 120;   /* over the HUD and its toasts */
  background: rgba(8,5,3,.55);
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
}
#layout-edit.hidden { display: none; }
/* while arranging, every control shows even if the game would hide it (no
   shield worn, nothing to move) — you can't place a button you can't see */
body.layout-edit #move-btn,
body.layout-edit #guard-btn,
body.layout-edit #playdead-btn,
body.layout-edit #dash-btn,
body.layout-edit #jump-btn { display: flex !important; opacity: 1 !important; }
body.layout-edit #touch-controls { display: block !important; }
body.layout-edit #move-btn.hidden,
body.layout-edit #guard-btn.hidden { display: flex !important; }

/* the bar is one tight row — a landscape phone has ~390px of height to spend
   and the controls being arranged live in the lower two thirds of it */
.le-bar {
  position: absolute; top: calc(8px + env(safe-area-inset-top)); left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: center;
  max-width: calc(100vw - 24px); padding: 8px 14px;
  background: rgba(31,22,12,.92); border: 2px solid var(--wood); border-radius: 16px;
  box-shadow: 0 6px 18px rgba(0,0,0,.45);
}
.le-title { font-weight: 800; font-size: .86rem; color: #ffe9b8; }
.le-row { display: flex; align-items: center; gap: 7px; }
.le-lb { font-size: .72rem; font-weight: 700; color: rgba(255,240,214,.8); }
.le-slider { width: 108px; accent-color: var(--gold); }
.le-val { font-size: .7rem; font-weight: 800; color: #ffe9b8; min-width: 38px; text-align: right; }
.le-row.off { opacity: .5; }
.le-row.off .le-slider { pointer-events: none; }
.le-btns { display: flex; gap: 7px; }
.le-btns .btn { padding: 6px 13px; font-size: .78rem; }
.le-hint {
  position: absolute; left: 50%; top: calc(64px + env(safe-area-inset-top)); transform: translateX(-50%);
  font-size: .72rem; font-weight: 700; color: rgba(255,247,232,.7);
  background: rgba(31,22,12,.6); padding: 4px 11px; border-radius: 999px;
  pointer-events: none; white-space: nowrap;
}
/* one handle per control, parked exactly over it (TouchLayout._syncHandles).
   The handle — not the button — takes the drag, so arranging the dash button
   never actually rolls the castaway. */
.le-h {
  position: absolute; border-radius: 50%; cursor: grab;
  border: 2px dashed rgba(255,225,150,.85);
  background: rgba(255,205,110,.16);
  box-shadow: 0 0 0 2px rgba(0,0,0,.25), 0 0 14px rgba(255,205,110,.35);
  touch-action: none;
}
.le-h .le-n {
  position: absolute; left: 50%; bottom: -19px; transform: translateX(-50%);
  font-size: .62rem; font-weight: 800; color: #ffe9b8; white-space: nowrap;
  text-shadow: 0 1px 3px #000; pointer-events: none;
}
/* the control the per-button size slider is aimed at */
.le-h.sel {
  border-style: solid; border-color: var(--gold, #ffcf6e);
  background: rgba(255,205,110,.24);
  box-shadow: 0 0 0 2px rgba(0,0,0,.3), 0 0 18px rgba(255,205,110,.55);
}
.le-h.sel .le-n { color: #fff6df; }
.le-h.dragging {
  cursor: grabbing; border-style: solid; border-color: #ffe9b8;
  background: rgba(255,205,110,.3);
  box-shadow: 0 0 0 2px rgba(0,0,0,.3), 0 0 22px rgba(255,205,110,.65);
}
/* very short landscape screens: shrink the chrome, never the handles */
@media (orientation: landscape) and (max-height: 430px) {
  .le-bar { top: calc(5px + env(safe-area-inset-top)); gap: 9px; padding: 6px 10px; }
  .le-title { font-size: .78rem; }
  .le-slider { width: 84px; }
  .le-hint { top: calc(50px + env(safe-area-inset-top)); font-size: .66rem; }
}
