/* ==========================================================================
   Goblin Miners — layout.css
   Base reset and the three-region shell.

   v1.2 arrangement (CONTRACT §4b): the rock is the centre of the screen and
   everything else surrounds it — core | mine | panel. The mine is the hero and
   gets the widest track; the two side columns are clamped so they stop growing.

   The app caps at 1600px and centres. The previous version was tuned at 1280
   and simply stretched, so on a 1900px monitor the mine became 37% of the
   screen and about 95% of it was empty. An idle game does not get better with
   more width, it gets emptier — past the cap the extra space becomes dark
   margin, which on this game reads as the cave closing in rather than as waste.

   Rule that outranks everything else here: the page never scrolls sideways at
   any width. Every grid and flex child carries min-width:0 so long numbers
   cannot push a track wider than its track definition.
   ========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  height: 100%;
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100%;
  overflow-x: hidden;
  background: var(--gm-bg);          /* explicit: this is a cave, never transparent */
  color: var(--gm-ink);
  font: 400 14px/1.45 var(--gm-font);
  /* Stop iOS from turning a fast tap-to-dig into a text selection or a zoom. */
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  overscroll-behavior: none;
}

/* Numbers must never jitter as they count. */
body { font-variant-numeric: tabular-nums; }

svg { display: block; image-rendering: pixelated; }

/* Keyboard users get a ring everywhere; mouse users never see it. */
:focus-visible {
  outline: 2px solid var(--gm-focus);
  outline-offset: 2px;
}

/* --------------------------------------------------------------- app shell */

.gm-app {
  /* The positioning context for the golden goblin, who is a child of this element
     since v1.18 and travels across all four columns. */
  position: relative;
  display: grid;
  gap: var(--gm-gap);
  height: 100vh;
  height: 100dvh;
  max-width: 1600px;
  margin-inline: auto;
  padding: var(--gm-gap);
  padding-top:    max(var(--gm-gap), env(safe-area-inset-top));
  padding-left:   max(var(--gm-gap), env(safe-area-inset-left));
  padding-right:  max(var(--gm-gap), env(safe-area-inset-right));
  padding-bottom: max(var(--gm-gap), env(safe-area-inset-bottom));

  /* FOUR COLUMNS SINCE v1.18, and two new rows with them. The owner: *"yo pondria un
     menu de botones a la izquierda del todo de la pantalla que te permitiese mostrar y
     ocultar todas esas ventanas"*, and separately that the totals were illegible in a
     300 px column.

     So: a narrow rail at the far left, the core column, the mine, the panel — and the
     figures strip spans the rail, the core and the mine underneath, which is where
     there is width for a dmg/s figure in the billions. The panel keeps both rows,
     because the tab pane is the one thing on screen that wants all the height it can
     get. Side columns still clamp; the mine still takes everything left over. */
  grid-template-columns:
    clamp(44px, 4%, 56px)
    clamp(220px, 19%, 300px)
    minmax(0, 1fr)
    clamp(316px, 27%, 400px);
  grid-template-rows: minmax(0, 1fr) auto;
  grid-template-areas:
    "rail core mine  panel"
    "rail core figs  panel";
}

.gm-app > * { min-width: 0; min-height: 0; }

.gm-mine    { grid-area: mine; }
.gm-core    { grid-area: core; }
.gm-panel   { grid-area: panel; }
.gm-rail    { grid-area: rail; }
.gm-figures { grid-area: figs; }

/* ---------------------------------------------------------------- the rail
   A column of icon buttons, one per window in the core column. It scrolls if it ever
   outgrows the viewport rather than pushing the app taller. */
.gm-rail {
  display: flex;
  flex-direction: column;
  gap: var(--gm-gap-sm);
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

/* --------------------------------------------------------------- mine view */

/* Rows: depth readout | the rock, vertically centred | DIG | the floor.
   The DIG row collapses to zero if the button has not been moved in yet, so
   this works whether .gm-dig sits here or still in .gm-core. */
.gm-mine {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  min-height: 0;
  /* .gm-mine__depth and .gm-morale both name `grid-row: 1; grid-column: 1`
     (R7) and so share one grid cell rather than two - .gm-morale sits over
     it with `justify-self: end`. ONE WRITER for how wide that reserved
     corner is, read by both (components.css): the layer name's right edge
     used to chase the same corner morale sits in, and a long seam name or
     a narrow panel made them overlap. */
  --gm-morale-w: min(240px, 55%);
}

/* Parallax plates sit behind everything and never take a pointer event. */
.gm-mine__sky,
.gm-mine__strata {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

/* v1.18.3: THE SAME BUG AS .gm-rock BELOW, IN THE ROW ABOVE IT. .gm-morale moved into
 * .gm-mine at v1.18 and correctly named `grid-column: 1` for itself (components.css) -
 * but it shares row 1 with this element, and naming the column on only ONE of two items
 * sharing a row is exactly the failure mode R7 exists to name: the other one is still
 * "explicitly placed" (it has a grid-row), so auto-placement does not skip it - it finds
 * column 1 already claimed and invents an implicit column 2 for THIS element instead.
 * That took every row with it, not just row 1: a second column track exists across the
 * whole grid the moment anything needs one, so .gm-rock's own column 1 (already named,
 * correctly) got squeezed into a fraction of the mine's width instead of all of it,
 * which is the "everything is off-centre" the owner saw. */
.gm-mine__depth { grid-row: 1; grid-column: 1; position: relative; z-index: 3; }

.gm-rock {
  grid-row: 2;
  /* Both this and .gm-goblins name the column explicitly, and they must.
     .gm-mine declares rows but no columns, so it is a one-column grid. Two items
     asking for `grid-row: 2` without a column do NOT land in the same cell: the
     first takes column 1 and auto-placement invents an implicit column 2 for the
     second. That is what put the crew in a narrow strip on the right and shoved
     the boulder — and the DIG button under it — off to the left.
     Naming it on both is not belt-and-braces: explicitly-placed items are placed
     before auto-placed ones, so fixing only .gm-goblins moves the rock instead. */
  grid-column: 1;
  position: relative;
  z-index: 3;
  justify-self: center;
  align-self: center;
}

.gm-mine > .gm-dig {
  grid-row: 3;
  /* Uncontested today - nothing else asks for row 3 - but R7 is "every explicitly
   * placed child", not "every child that currently shares a row with another one".
   * Named on sight rather than left to keep working by accident, the way
   * .gm-mine__depth just proved that assumption does not survive the next addition. */
  grid-column: 1;
  position: relative;
  z-index: 3;
  justify-self: center;
  width: min(320px, 90%);
  margin-bottom: var(--gm-gap);
}

/* The goblins are a working crew around the rock, not a status bar under the
   button (HANDOFF section 4.2). The layer therefore SHARES THE ROCK'S GRID AREA
   rather than owning a row of its own: the rock is centred in row 2, this layer
   stretches to fill row 2, so the centre of this box is the centre of the rock
   at every breakpoint and in every degenerate case. On a short phone where row 2
   collapses to nothing and the rock overflows it, the crew overflows with it and
   stays in register — which no amount of fixed offsets from the mine's own box
   could manage.

   Row 4 still exists and now collapses to zero. Nothing else may be put in it
   without re-checking the arithmetic in components.css.

   pointer-events: none is load-bearing, not defensive. The rock is a button and
   the whole point is to crowd it; a goblin box overlapping the rock must not
   swallow the tap that breaks it. */
/* The layer is inert; the goblins in it are NOT (v1.14).

   The owner: *"no dicen nada ni se ve un nombre cuando pongo el cursor encima"*. The
   names and one-liners were written in v1.12 and have never once been reachable,
   because `pointer-events: none` sat on this container and every goblin inherited it,
   so `pointerover` could not land on one and the tooltip rule could not fire. A
   feature nobody could touch, shipped and reported green, for two versions.

   The container stays inert so the empty space around the crew still digs - the mine
   is one big tap target and taking that away to add a tooltip would be a bad trade -
   and only the sprites themselves take the pointer. ui.js forwards a press on a
   goblin to the same dig handler the rock uses, so hovering gains you a name and
   pressing still swings the pick. */
.gm-goblins {
  grid-row: 2;
  grid-column: 1;        /* same cell as the rock - see the note on .gm-rock */
  position: relative;
  z-index: 3;
  align-self: stretch;
  justify-self: stretch;
  pointer-events: none;
}

.gm-goblin { pointer-events: auto; cursor: pointer; }

/* Effects layer covers the whole mine and is inert. */
.gm-fx {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  overflow: hidden;
}

/* v1.18: he crosses the whole app now, so he is positioned against it rather than
   against the mine - which is `overflow: hidden` and was the fence. z-index above
   everything except the modal and the ascension sky, because a thing you have to catch
   must not be catchable only where nothing else is drawn. */
.gm-golden { position: absolute; z-index: 45; }

/* MANI, on the rock (v1.18). He shares the rock's grid cell so that he sits ON the
   boulder at every breakpoint - the same trick .gm-goblins uses, and for the same
   reason: an offset measured from the mine's own box drifts the moment the rock
   resizes. Named column, or the boulder goes sideways (layout.css, .gm-rock). */
.gm-mani {
  grid-row: 2;
  grid-column: 1;
  position: relative;
  z-index: 4;
  justify-self: center;
  align-self: center;
  /* Up and to the right of centre, in units of the rock, so he perches on the
     shoulder of it rather than standing in front of the health bar. */
  translate: calc(var(--gm-rock-size) * 0.30) calc(var(--gm-rock-size) * -0.34);
  pointer-events: auto;
  cursor: help;
}

.gm-mani svg {
  width: calc(var(--gm-rock-size) / 4);
  height: calc(var(--gm-rock-size) / 4);
}


/* ------------------------------------------------------------- core column */

.gm-core {
  display: flex;
  flex-direction: column;
  gap: var(--gm-gap);
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

/* The chest heads the column and is sized by its contents — ten cells, never
   more — so it must not absorb slack the way a flex child otherwise would. */
.gm-chest { flex: none; }

/* The ore cart lists what it holds and no more — it must not stretch its rows
   to fill the column, which is what left a single chip alone in a tall box.
   The wagon is the flex child now; the grid inside it is not. The bottom margin
   is the 9px the wheels hang below the body (components.css). */
.gm-cart { flex: 0 1 auto; margin-bottom: 9px; }
.gm-ores { align-content: start; }

/* The figures band LEFT this column in v1.18 - it is a wide strip under the mine now
   (see the grid areas above). The `margin-top: auto` that used to push it to the foot
   went with it: the column is a plain stack again, and what holds the screen top to
   bottom is the grid rather than one flex child absorbing the slack. */

/* Fallback only: until .gm-dig moves into .gm-mine it still lives here. */
.gm-core > .gm-dig { flex: 1 1 auto; min-height: 160px; max-height: 280px; }

/* -------------------------------------------------------------- side panel */

.gm-panel {
  display: flex;
  flex-direction: column;
  gap: var(--gm-gap);
  min-height: 0;
}

.gm-tabpanes {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
}

/* ====================================================================== */
/* Tablet — the mine keeps top billing across the full width.             */
/* ====================================================================== */

@media (max-width: 1023px) {
  .gm-app {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto minmax(210px, 34vh) auto minmax(0, 1fr);
    grid-template-areas:
      "rail rail"
      "mine mine"
      "figs figs"
      "core panel";
  }

  /* The rail folds to a strip of buttons across the top rather than a tall thin
     column: at this width a 44 px column is a tenth of the screen spent on five
     buttons, and the mine is the thing that should have it. */
  .gm-rail {
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
  }

  .gm-core > .gm-dig { min-height: 120px; }
}

/* ====================================================================== */
/* Phone — one screen at a time, switched by the tab bar (THE MOBILE HUD)   */
/* ====================================================================== */

/* v1.18.6. Two earlier rounds both tried to fit the desktop's five regions
   (rail, core, mine, figures, panel) onto one phone-width column at once - first
   by giving the mine a taller floor, then by letting the whole page grow past
   one screen instead of squeezing `panel` to zero. Neither actually solved it:
   the owner, on a real phone, after both had shipped: *"no funciona nada, no hay
   ni forma de scrollear, haz un hud completo especial para movil"*. Whatever was
   stopping the page itself from scrolling never reproduced in a DevTools device
   toolbar, so the actual cause was never confirmed - but it does not matter
   which it was, because a page tall enough to hold five desktop regions stacked
   was always going to be a lot of scrolling to reach the shop regardless of the
   cause. A phone does not have room to show all five at once and neither
   approach changed that fact, only where the overflow went.

   So this is not a third attempt at the same idea, tighter. It is the one
   thing a phone screen can actually hold: ONE region at a time, chosen by the
   tab bar - already docked to the floor since v1.12, and now also a mode
   switch (see the new "mine" tab in index.html, `ui.renderTab()` writing
   `data-active-tab` onto #gm-app, and the selectors below that key off it).
   The resource strip is pinned to the ceiling the same way the tab bar is
   already pinned to the floor, so gold is always on screen whichever region is
   showing. #gm-app itself is the thing that scrolls now - fixed at the
   viewport height, `overflow-y: auto` - rather than the page underneath a
   fixed bar, which is the part that stopped working. A self-contained scrollbox
   does not depend on whatever the page/embed around it does or does not allow. */
@media (max-width: 639px) {
  .gm-app {
    display: block;
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    /* Clears the fixed resource strip and the fixed tab bar (components.css) at
       the top and bottom of whatever is currently scrolled into view - the same
       job the old grid-template-rows/-areas did, done with padding on the one
       box that scrolls instead of tracks on a grid that no longer exists here. */
    padding-top: calc(var(--gm-resbar-h) + var(--gm-gap-sm));
    padding-bottom: calc(var(--gm-tabbar-h) + var(--gm-gap-sm) + env(safe-area-inset-bottom));
  }

  /* One button per window in the core column, meant for a desktop with shared
     screen space to reclaim (see the rail's own note in this file). A phone
     gives the chest, the cart and the market their own screen instead - the
     "mine" tab, components.css - so nothing in there is ever competing for room
     and there is nothing left for the rail to fold. */
  .gm-rail { display: none; }

  /* Was minmax(310px, 42vh) - a number budgeted for competing with core, figs
     and panel inside one fixed screen, in the round before this one. The mine
     now gets the whole screen to itself whenever it is the active tab
     (components.css), so it is sized generously instead of rationed: tall
     enough to read as the hero view a phone idle game opens on, short enough
     that the chest/cart/market riding below it (same screen, same scroll) are
     one swipe away rather than a second screen's worth of travel. */
  .gm-mine { min-height: 72vh; }

  /* No longer internally scrolling (overflow: visible) - #gm-app itself is the
     one scroll container on a phone now (above), and a nested scrollbox here
     would fight it for the same drag gesture instead of just extending it. */
  .gm-core { overflow: visible; }

  /* v1.20: THE SAME TRAP, ONE ELEMENT OVER. .gm-tabpanes keeps its base rule's
     own `overflow-y: auto` (below, shared with desktop/tablet where .gm-panel
     IS height-bound by the grid and this is the thing that correctly scrolls) -
     but on a phone #gm-app is the one scroll container now, same reasoning as
     .gm-core just above, and this got missed when that rule was written. A
     short pane (Settings) never showed it; a long one (Awards, Stats) is where
     the drag stopped reaching the outer scrollbox - the owner: *"no funciona el
     scroll de movil de muchas cosas como los logros y los stats"*. */
  .gm-tabpanes { overflow: visible; overscroll-behavior: auto; }

  .gm-core > .gm-dig { min-height: 116px; max-height: none; }

  .gm-mine > .gm-dig { width: min(280px, 94%); }

  /* Out of the panel, onto the viewport floor - unchanged since v1.12. It is
     also the mode switch for which region #gm-app shows now (components.css),
     which is one more reason it can never be the thing that scrolls out of
     reach. */
  .gm-tabs {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* Keep the bar itself from ever measuring shorter than the token the rest of
     the phone layout reserves room against (theme.css, ui.js measure()) - see
     the v1.12 note this rule has always carried: a bar measured 60px against a
     token of 62 once, and the gap was enough for the last row of a long pane to
     sit under the buttons. */
  .gm-tabs { min-height: var(--gm-tabbar-h); }
}

/* Very short landscape phones only (portrait phones never match max-height:
   460px). Narrowed to exclude the phone breakpoint above in v1.18.6 - it used
   to resize a grid row inside .gm-app, and .gm-app is no longer a grid at
   639px and below (above), so that row would now be silently inert there. The
   one-screen-at-a-time HUD does not need a landscape-specific squeeze the old
   stacked layout did: there is no second region competing with the mine for
   height, only more of the same screen to scroll to reach it. */
@media (max-height: 460px) and (min-width: 640px) and (max-width: 1023px) {
  .gm-app { grid-template-rows: minmax(160px, 28vh) auto minmax(0, 1fr); }
}
