:root {
  --accent: #3ddc5a;
  --accent-dark: #1f7a34;
  --bg: #0b0e0b;
  --panel: #11150f;
  --wall: #6b7a6b;
  --text: #d7e6d7;
}

/* touch-action does NOT inherit: `manipulation` must land on every element
   or Safari still double-tap-zooms on DOM overlays even though body has it
   (same bug + fix as metro2's 2026-07-13 user report). */
* {
  box-sizing: border-box;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'SF Mono', 'Menlo', 'Consolas', 'DejaVu Sans Mono', monospace;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 12px;
  gap: 12px;
}

.hidden { display: none !important; }

/* ---- Language picker (top-right) — same pattern as every other game ---- */
#langToggle {
  position: fixed;
  top: max(10px, env(safe-area-inset-top));
  right: max(10px, env(safe-area-inset-right));
  z-index: 60;
  display: flex;
  gap: 2px;
  padding: 3px;
  border: 2px solid var(--accent);
  border-radius: 14px;
  background: var(--panel);
  box-shadow: 0 4px 0 rgba(0, 0, 0, 0.4);
}

#langToggle button {
  font-family: inherit;
  font-size: clamp(11px, 2.6vw, 13px);
  font-weight: 800;
  padding: 5px 8px;
  border: none;
  border-radius: 11px;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  white-space: nowrap;
}

#langToggle button.active { background: var(--accent); color: #04220c; }
#langToggle button:active { transform: translateY(1px); }

.screen {
  width: 100%;
  max-width: 720px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  flex: 1 1 auto;
  min-height: 0;
}

/* ---- Menu ---- */
#menu h1 {
  font-size: clamp(28px, 8vw, 40px);
  margin: 40px 0 0;
  letter-spacing: 2px;
  color: var(--accent);
  text-shadow: 0 0 12px rgba(61, 220, 90, 0.4);
}

#menu .subtitle {
  margin: 0;
  opacity: 0.75;
  text-align: center;
  font-size: 14px;
}

#menu .credit {
  margin: 24px 0 0;
  font-size: 12px;
  opacity: 0.55;
  text-align: center;
  max-width: 480px;
}

.big-btn {
  font-family: inherit;
  font-size: 18px;
  font-weight: 700;
  padding: 14px 32px;
  border: 2px solid var(--accent);
  border-radius: 10px;
  background: var(--accent);
  color: #04220c;
  cursor: pointer;
}

.big-btn.secondary {
  background: transparent;
  color: var(--accent);
}

/* ---- Game screen ----
   #game fills exactly the viewport height (no page scroll — see html/body's
   overflow: hidden above); mapWrap is the ONLY scrollable region within it,
   so a too-tall or too-wide dungeon never forces a second, page-level scroll
   fighting the map's own drag/scroll (user report 2026-07-13).
   padding-top clears the fixed top-left stack — #backToHub (~0.9rem offset,
   ~2rem tall) with #fsToggle right below it (shared/fullscreen.js stacks it
   at +2.9rem, itself 2.1rem tall, so it reaches ~5.9rem down) — plus
   #langToggle on the right (~46px). The left stack is taller, so it's the
   binding constraint; without this, msgLine renders underneath #fsToggle's
   circular button (user report 2026-07-13, real-device screenshot showed
   the welcome message's first few letters hidden behind it). */
#game {
  width: 100%;
  max-width: 820px;
  align-items: stretch;
  overflow: hidden;
  padding-top: calc(max(0.9rem, env(safe-area-inset-top)) + 5.9rem + 10px);
}

#msgLine {
  font-size: 14px;
  min-height: 1.4em;
  padding: 2px 6px;
  white-space: pre;
  overflow-x: auto;
  flex-shrink: 0;
}

/* flex-grow:0 — mapWrap must NOT stretch to fill leftover vertical space:
   the dungeon grid is a fixed 80x22 size, so once its own content (mostly
   blank unseen tiles) is rendered at its natural size, any extra flex space
   should shrink the box's dead margin, not balloon the box itself into a
   mostly-empty rectangle around a tiny map (user report 2026-07-13,
   real-device screenshot). flex-shrink:1 + min-height:0 + overflow:auto
   still let it shrink and internally scroll if the viewport is too short. */
#mapWrap {
  width: 100%;
  flex: 0 1 auto;
  min-height: 0;
  overflow: auto;
  background: #000;
  border: 1px solid var(--wall);
  border-radius: 4px;
  padding: 4px 0;
}

#map {
  display: block;
  position: relative; /* offsetParent for the hero span — scrollHeroIntoView measures .at's offsetLeft/Top against #map */
  font-size: clamp(10px, 2.4vw, 16px);
  line-height: 1.15;
  white-space: pre;
  width: max-content;
  margin: 0 auto;
  color: var(--text);
}

#map .row { display: block; }
#map .at { color: #fff; font-weight: 700; }
#map .gold { color: #e8c547; }
#map .mon { color: #ff6b6b; font-weight: 700; }
#map .mon.detect { color: #c084fc; } /* monster-detection: sensed but unseen */
#winSaleList { max-height: 40vh; overflow-y: auto; font-size: 14px; text-align: left; font-family: var(--mono, monospace); }
#winSaleList p { margin: 2px 0; white-space: pre; }
#map .wall { color: var(--wall); }
#map .door { color: #c98a4b; }
#map .stairs { color: #7fd7ff; }
#map .trap { color: #e05b5b; }

#statusLine {
  font-size: 13px;
  padding: 4px 6px;
  opacity: 0.9;
  white-space: normal;
  word-spacing: 0.3em;
  line-height: 1.5;
  flex-shrink: 0;
}

/* ---- Controls (joystick + action bar) ----
   The site-wide feedback bubble is hidden for the whole #game screen (see
   js/main.js) rather than carved around here — it only ever needs to sit
   near the action bar on the #menu screen, where there's nothing to hide
   behind it (user decision 2026-07-14, after two rounds of position
   juggling: bottom-right collided with Up, bottom-left collided with
   statusLine). Just clear the home-indicator safe area. */
#controls {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
  flex-shrink: 0;
  padding-bottom: env(safe-area-inset-bottom);
}

/* Virtual joystick — drag from center, like a mobile MOBA movement stick.
   Pointer Events (not click) so touch-drag is tracked reliably even if the
   finger moves outside the base; see js/main.js's setPointerCapture use. */
#joystick {
  position: relative;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, rgba(61, 220, 90, 0.08), rgba(17, 21, 15, 0.9));
  border: 2px solid var(--accent);
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  flex-shrink: 0;
}

#joystickKnob {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 56px;
  height: 56px;
  margin: -28px 0 0 -28px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  pointer-events: none;
  transition: transform 0.06s linear;
}

#joystick.active #joystickKnob { transition: none; }

/* ---- Action bar (inventory / stairs) ---- */
#actionBar {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
}

#actionBar button {
  font-family: inherit;
  font-size: 13px;
  padding: 8px 12px;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: var(--panel);
  color: var(--accent);
  cursor: pointer;
}

/* ---- Overlays (inventory, death/score) ---- */
.overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 80;
  padding: 16px;
}

.overlay-card {
  background: var(--panel);
  border: 2px solid var(--accent);
  border-radius: 10px;
  padding: 20px;
  max-width: 480px;
  width: 100%;
  max-height: 80vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.overlay-card h2 { margin: 0; color: var(--accent); }

/* The overlay sits above #msgLine (z-index), so refusal messages like "you
   are already wearing some" set on #msgLine while the inventory is open are
   otherwise invisible until the player closes it (user report 2026-07-15,
   "trying to wear an armor did nothing" — the wear was correctly refused,
   the refusal message just never rendered anywhere visible). */
#invMsg { margin: -4px 0 0; font-size: 13px; color: var(--accent); text-align: center; }

#invList { display: flex; flex-direction: column; gap: 4px; font-size: 14px; }

#invList button {
  font-family: inherit;
  text-align: left;
  background: transparent;
  border: 1px solid var(--wall);
  border-radius: 6px;
  color: var(--text);
  padding: 8px 10px;
  cursor: pointer;
}

#invList button:hover { border-color: var(--accent); }

#invActions { display: flex; gap: 6px; flex-wrap: wrap; }

#invActions button {
  font-family: inherit;
  font-size: 13px;
  padding: 8px 12px;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: var(--accent);
  color: #04220c;
  cursor: pointer;
}

/* ---- Landscape layout: side-by-side map + controls, sized to the
   viewport height instead of stacking. A short landscape viewport can't
   fit msgLine + 22 map rows + statusLine + joystick + action bar stacked
   vertically without cutting something off or forcing page scroll.

   #game's height and #map's font-size both need the same vh → dvh upgrade
   html/body already got: plain `vh` is Safari's *largest* viewport (as if
   its address bar were hidden), so while the address bar is visible (the
   common case, not full-screen) `vh` overshoots the actually-visible area.
   That made #game taller than what's really on screen, and (more
   noticeably) inflated #map's font-size/height past what fits in the
   allotted grid row — read on a real device as a tiny map adrift in a huge
   black box (user report 2026-07-13). `dvh` tracks the real, current
   viewport, so both dimensions now shrink together as the browser chrome
   shows/hides, instead of #map assuming more space than #game actually has. */
@media (orientation: landscape) {
  body { padding: 6px; gap: 6px; }

  /* Landscape reclaims the tall portrait padding-top (reserved there to
     clear the fixed back/fullscreen stack): a phone's landscape viewport is
     only ~300px tall, and ~118px of padding pushed the action buttons off
     the bottom (caught by scripts/playtest-rogue.mjs, matching the user's
     real-device screenshot). Instead the message line side-steps the
     back-to-hub pill horizontally; the fullscreen toggle floats over the
     map's (virtually always empty) top-left corner. */
  #game {
    max-width: 100%;
    height: calc(100vh - 12px);
    height: calc(100dvh - 12px);
    padding-top: max(0.4rem, env(safe-area-inset-top));
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
      "msg    msg"
      "map    controls"
      "status controls";
    gap: 6px;
  }

  #msgLine { grid-area: msg; font-size: 12px; min-height: 1.2em; margin-left: 8.5rem; }
  #statusLine { grid-area: status; font-size: 11px; }

  /* Plain block + overflow (NOT display:flex with justify-content:center):
     flex-centering an overflowing child makes part of the overflow
     unreachable AND shifts the scroll-coordinate origin, which made the
     hero-follow camera lurch wildly — read on a real device as "my
     character teleports between rooms when I move" (user report
     2026-07-14). #map's own margin:0 auto centers it when it fits; when it
     overflows, content starts at scrollLeft 0 like any normal scroller and
     scrollHeroIntoView's math holds. */
  #mapWrap {
    grid-area: map;
    min-height: 0;
    min-width: 0;
    overflow: auto;
  }

  /* Sized off width, not height: fitting all 22 rows into the short
     landscape row (via vh/dvh or container-query height) forced a font so
     small it was unreadable on a real device (user report 2026-07-14).
     Legibility wins over seeing the whole map at once — mapWrap already
     scrolls (align-items:flex-start above ensures nothing above the fold
     is unreachably clipped), so a map taller than its row just scrolls,
     same as a too-wide one already does. */
  #map { font-size: clamp(12px, 1.8vw, 18px); }

  /* justify-content:flex-start + overflow-y:auto, NOT center: centering an
     overflowing flex column hides the top half of the overflow above the
     scrollable area, unreachable (same bug class as mapWrap's fix above —
     user report 2026-07-14, real-device screenshot showed Down/Up pushed
     off-screen once the action bar grew to five buttons). */
  #controls {
    grid-area: controls;
    margin-top: 0;
    justify-content: flex-start;
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    padding-bottom: 0;
    gap: 8px;
  }

  #joystick { width: 100px; height: 100px; }
  #joystickKnob { width: 42px; height: 42px; margin: -21px 0 0 -21px; }

  /* Two columns so five buttons fit beside the joystick in a short
     landscape viewport (a single column stacked ~190px of buttons under a
     100px joystick — taller than an iPhone's landscape height). */
  #actionBar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
  }

  #actionBar button {
    font-size: 12px;
    padding: 6px 8px;
    white-space: nowrap;
  }
}
