/* Minhocário — per-screen layout.
   Cascade position 4 of 5: tokens → base → components → screens → motion.
   index.html is the source of truth for that order; never use @import.

   Layout and screen-specific chrome for home / shop / setup / game. Loaded
   after components.css so a screen can position a component without raising
   specificity. */

/* --- Home --- */
.home {
  max-width: 640px;
  margin: 0 auto;
  text-align: center;
}

/* The micro-label: uppercase, tracked, one step below the workhorse. It is the
   field-instrument register's smallest voice — a caption on a dial, not a
   sentence — and it recurs on every screen, so it is written once here. */
.lang-select__label,
.ranking h2,
.ranking__table th,
.wallet,
.setup legend {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--ink-dim);
}

/* Language selector: a native-name button group. Home-only (spec fork 2).
   A segmented control — one hairline box with rules BETWEEN the rungs, rather
   than a border around each: three separate buttons read as three decisions,
   one segmented box reads as a single choice with three positions. */
.lang-select {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}
.lang-select__options {
  display: flex;
  border: 1px solid var(--surface-2);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.lang-select__btn {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  color: var(--ink-dim);
  background: transparent;
  border: 0;
  border-left: 1px solid var(--surface-2);
  border-radius: 0;
}
.lang-select__btn:first-child { border-left: 0; }
/* The base `button:hover` lights the whole border accent, which here would draw
   a green line down the middle of the control. The fill carries the hover. */
.lang-select__btn:hover { background: var(--surface-3); color: var(--ink); border-color: var(--surface-2); }
.lang-select__btn.is-active { background: var(--accent); color: var(--accent-ink); }

/* The brand mark. Sized by width alone — `height: auto` lets the viewBox's own
   ratio (54:43) do the rest, so retuning the artwork never needs a second edit
   here. 112px is a component dimension, not a step on the spacing scale, which
   is why it is a literal (see the ALLOWED_LENGTHS note in tests/css.test.js). */
.home__mark {
  display: block;
  width: 112px;
  height: auto;
  margin: 0 auto var(--space-4);
  color: var(--accent);
}
/* The eye and the two segment marks are knock-outs: they are the page showing
   through the worm, so this must stay whatever body paints behind it.
   tests/css.test.js asserts the two are the same token — get it wrong and the
   worm grows three slightly-off-colour blobs that nothing else reports. */
.home__mark-cut { color: var(--surface-0); }

.home__title { margin-bottom: var(--space-1); }
.home__subtitle { color: var(--ink-dim); font-size: var(--text-md); margin: 0; }
.home__actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  margin: var(--space-6) 0;
}
.home__actions button { background: var(--accent); color: var(--accent-ink); }

/* The nickname reads as a stamped identity plate rather than a sentence — it is
   the one piece of player data on the screen. */
.home__nickname {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding: var(--space-1) var(--space-1) var(--space-1) var(--space-3);
  color: var(--ink-dim);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  background: var(--surface-1);
  border: 1px solid var(--surface-2);
  border-radius: var(--radius);
}
.home__nickname strong {
  color: var(--ink);
  font-size: var(--text-md);
  text-transform: none;
  letter-spacing: normal;
}
.home__nickname button { padding: var(--space-1) var(--space-3); font-size: var(--text-2xs); }
.home__notice {
  color: var(--state-alert-ink);
  background: var(--state-alert-bg);
  border: 1px solid var(--state-alert);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
}
.home__notice[hidden] { display: none; }

.ranking { text-align: left; margin-top: var(--space-8); }
.ranking h2 { margin-bottom: var(--space-2); }
.ranking__table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.ranking__table th,
.ranking__table td {
  text-align: left;
  padding: var(--space-2);
  border-bottom: 1px solid var(--surface-2);
}
/* Score and days are readouts, so they get the readout treatment: right-aligned
   and tabular, which turns the column into a stack of comparable magnitudes
   instead of a ragged list of strings. js/ui/home.js tags the cells. */
/* Scoped through .ranking__table deliberately: the `th, td` rule above sets
   text-align: left at (0,1,1), so a bare .ranking__num (0,1,0) would lose. */
.ranking__table .ranking__num { text-align: right; font-variant-numeric: tabular-nums; }
.ranking__table tbody tr:hover { background: var(--surface-1); }
.ranking__empty { color: var(--ink-faint); font-size: var(--text-sm); }

/* --- Shop / Setup --- */
.shop__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-4);
  max-width: 900px;
  margin: 0 auto;
}
.wallet {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  background: var(--surface-1);
  border: 1px solid var(--surface-2);
  border-radius: var(--radius-sm);
}
#shop-wallet {
  color: var(--accent);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: normal;
}

.setup { max-width: 640px; margin: 0 auto; display: grid; gap: var(--space-4); }
.setup fieldset {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4) var(--space-4);
  background: var(--surface-1);
  border: 1px solid var(--surface-2);
  border-radius: var(--radius);
}
.setup legend { padding: 0 var(--space-2); }
/* The primary action of the whole pre-game flow, and until now an unstyled grey
   button identical to Reroll — the "completes setup unaided" criterion (T22). */
#setup-confirm {
  justify-self: start;
  padding: var(--space-2) var(--space-6);
  background: var(--accent);
  color: var(--accent-ink);
}

.setup-species {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
/* --surface-3's first user. It is the ramp's "raised/hover" step and has sat
   unmeasured since V2b; note that only --ink, --ink-dim and --accent may sit on
   it (see the contrast map in tests/css.test.js) — all three are what a species
   row contains, which is why the hover landed here and not on the shop card. */
.setup-species:hover { background: var(--surface-3); }
/* The radio dot alone is a 13px signal for the screen's biggest decision. An
   accent rule down the selected row says it at a glance. */
.setup-species:has(input:checked) {
  background: var(--surface-2);
  box-shadow: inset 2px 0 0 var(--accent);
}
.setup-species__body { display: grid; gap: var(--space-05); }
.setup-species__latin { color: var(--ink-dim); font-size: var(--text-sm); }
.setup-species__desc { font-size: var(--text-md); color: var(--ink-dim); }
.setup-species__price {
  color: var(--accent);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}

.setup-bedding__row,
.setup-waste__amount {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-05) 0;
  font-size: var(--text-sm);
}
.setup-bedding__row input,
.setup-waste__amount input { width: 90px; text-align: right; }
#setup-waste { display: grid; gap: var(--space-2); }

/* --- Desktop-only gate --- */
/* Hidden for everyone by default; the media query below is the ONLY thing that
   reveals it. Declared here rather than in components.css because it is a
   screen — the last screen a phone ever gets. */
#desktop-only {
  display: none;
  min-height: 100vh;
  place-content: center;
  gap: var(--space-4);
  padding: var(--space-8);
  text-align: center;
  background: var(--surface-0);
}
/* The wordmark, and the real <h1> of the document — see the reasoning in
   index.html. The notice itself is the <h2> below it. */
#desktop-only h1 {
  font-size: var(--text-xl);
  color: var(--ink);
}
#desktop-only h2 {
  font-size: var(--text-lg);
  color: var(--ink);
}
#desktop-only p {
  max-width: 40ch;
  margin: 0 auto;
  font-size: var(--text-md);
  line-height: 1.5;
  color: var(--ink-dim);
}
/* One step up from the body copy: this line is the first thing that says what
   the game is, on the only screen a phone ever gets. */
#desktop-only .desktop-only__lead {
  font-size: var(--text-base);
  color: var(--ink);
}

/* The gate itself. `(pointer: coarse) and (hover: none)` is a device whose
   primary input is a finger — phones and tablets, iPadOS included (it reports
   itself as macOS, so a user-agent check would miss it) — while a touchscreen
   laptop keeps its mouse, so it hovers, so it plays.

   This query is stated a SECOND time in js/ui/platform.js, which is unavoidable:
   CSS paints the notice, JS declines to boot the WebGL scene behind it. The two
   are held character-for-character identical by tests/platform.test.js, because
   drift is silent in both directions — a looser CSS query walls off someone the
   game would have run for, a looser JS query boots a scene nobody can see. */
@media (pointer: coarse) and (hover: none) {
  #desktop-only { display: grid; }
  #app { display: none; }
}

/* --- Game screen grid --- */
/* Three columns (V12). Left: the internals/x-ray readouts, promoted out of the
   canvas overlay — the densest content on screen was in the most constrained
   container, and moving it is the single biggest density win available.
   Centre: the 3D stage and the colony-dead banner. Right: actions and stats.
   HUD and speed bar span all three.

   `minmax(0, 1fr)` on the stage column AND row is load-bearing: a bare `1fr` is
   `minmax(auto, 1fr)`, whose auto minimum refuses to shrink below the content's
   size, so the canvas would push the grid wider than the viewport instead of
   giving way. Named areas are checked both ways by tests/css.test.js — a
   `grid-area` typo places the element in an implicit track rather than warning,
   which on a WebGL region reads as a stretched scene, not as a CSS bug. */
.screen--game {
  padding: 0;
  height: 100vh;
  display: grid;
  grid-template-columns: minmax(280px, 340px) minmax(0, 1fr) minmax(260px, 320px);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    'hud      hud    hud'
    'readouts stage  actions'
    'speed    speed  speed';
}

/* Collapsing the readouts hands the column back to the 3D scene, which is the
   behaviour the panel had as an overlay and would otherwise have lost — a fixed
   track would leave a 280px empty gutter instead. `auto` sizes the column to the
   collapsed <summary>.

   This is also what keeps V12's own acceptance criterion honest: "drag stays
   pinned with panels collapsed/expanded" can only fail if collapsing actually
   resizes the canvas, and with fixed tracks it never does. The resize is picked
   up by the ResizeObserver added in V5 — a window-resize listener alone would
   miss it, which is precisely the stale-`camera.aspect` bug V5 fixed.

   `:has()` degrades silently where unsupported (the layout simply keeps the
   fixed column), so this is a progressive enhancement, not a dependency. */
.screen--game:has(#internals:not([open])) {
  grid-template-columns: auto minmax(0, 1fr) minmax(260px, 320px);
}

/* V13: widen the readouts track at wide viewports so its sub-grid has room to
   go two-up. Without this the density pass is inert — `auto-fit` lays out one
   column at every width, silently, because two 220px columns plus their gap
   cannot fit inside the 340px the track is capped at below this breakpoint.
   1600px is the width at which surrendering ~200px of stage buys a second
   readout column and still leaves the centre wider than the sidebars.

   Lower specificity than the `:has()` rule above by design: a collapsed panel
   keeps its `auto` track at every viewport, wide ones included. */
@media (min-width: 1600px) {
  .screen--game {
    grid-template-columns: minmax(480px, 560px) minmax(0, 1fr) minmax(260px, 320px);
  }
}

/* V20: below the width where three columns stop fitting, the game screen stacks.
   Found by the release audit, not by a checkpoint — CPV1–CPV4 were all walked on
   desktop, and this is invisible from one.

   The three tracks above carry FIXED minimums (280 + 260), and a grid track is
   never shrunk below its minmax minimum. So under 540px the page overflowed
   sideways AND the stage — `minmax(0, 1fr)`, correctly floorless — resolved to
   zero width: no 3D scene at all, which is the spec's mobile acceptance
   criterion. v1's `1fr 260px` had no such floor and simply gave way, so this
   arrived with V12 and nothing could report it: not a parse error, not a visual
   break on the desktop it was designed for. `tests/css.test.js` now asserts the
   arithmetic at 360px, the narrowest viewport still in real use.

   Stacked order puts the two controls you reach for while watching the scene
   directly under it (speed, then actions) and the reference panel last — it is a
   <details> the player can collapse, and on a phone it is the one band that can
   afford to be scrolled to.

   `:has()` is repeated deliberately. It takes the specificity of its argument,
   so `.screen--game:has(#internals:not([open]))` carries an ID and would beat a
   plain `.screen--game` here — a media query adds no specificity. Without the
   second selector this rule would fix the layout only while the panel is open,
   and collapsing it on a phone would silently restore the broken grid. That is
   V13's specificity note in the opposite direction: there the wide rule is
   deliberately WEAKER so a collapsed panel keeps its `auto` track; here the
   narrow rule must be strong enough to win in both states. */
@media (max-width: 899px) {
  .screen--game,
  .screen--game:has(#internals:not([open])) {
    /* `height` becomes a floor: the bands size to their content and the page
       scrolls, rather than five rows being crushed into one viewport. */
    height: auto;
    min-height: 100vh;
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 55vh auto auto auto;
    grid-template-areas:
      'hud'
      'stage'
      'speed'
      'actions'
      'readouts';
  }
  /* Side hairlines separated columns; stacked, the same job is done from above.
     Left as borders rather than dropped, so each band still reads as its own
     surface against the stage. */
  .internals { border-right: 0; border-top: 1px solid var(--surface-2); }
  .actions { border-left: 0; border-top: 1px solid var(--surface-2); }
}

/* An instrument strip: hairline-divided cells rather than free-floating chips.
   The rules are what make it read as one panel of six gauges instead of six
   unrelated labels, and they cost nothing but a border. */
.hud {
  grid-area: hud;
  display: flex;
  flex-wrap: wrap;
  padding: 0 var(--space-2);
  background: var(--surface-1);
  border-bottom: 1px solid var(--surface-2);
}
/* Same clearance deal as `.screen` — only dev mode has a bar to clear, and 44px
   is measured off that bar rather than picked from the scale. Vertical padding
   lives on the cells, so this stays a pure top offset. */
body.dev-mode .hud { padding-top: 44px; }
.hud__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  color: var(--ink-dim);
  border-right: 1px solid var(--surface-2);
}
.hud__item:last-child { border-right: 0; }
.hud__label {
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}
/* Carried by every field updateHud writes — tests/markup.test.js derives that
   list from hud.js and fails if one is missing the class.

   `min-width` reserves four digits so a score crossing 99 -> 100 does not shove
   every cell to its right. Tabular numerals alone only stop digits from
   shifting WITHIN a number; the strip still re-flowed each time one grew. Past
   four digits it re-flows once more, which is a rare event rather than the
   per-tick twitch this exists to stop. */
.hud__value {
  min-width: 4ch;
  color: var(--ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
/* The nickname is set once and never ticks, so it needs neither the reserved
   width nor the alignment that reservation implies. */
.hud__item--player .hud__value { min-width: 0; text-align: left; }

/* Placement only — the panel's own appearance is in components.css, which is
   where it stays reusable. Every region of this grid claims its area here, next
   to the template that declares it; tests/css.test.js checks the two match. */
.internals { grid-area: readouts; }

/* V13 density pass. The four groups are independent blocks, so letting them
   flow into columns costs one rule — `auto-fit` collapses to a single column on
   its own wherever a second will not fit, which is what makes 1366px degrade
   gracefully without a second breakpoint.

   `align-items: start` keeps each group its natural height; stretching them to
   match the tallest (the food queue) would open ragged gaps under the short
   ones. The gap replaces the per-group `margin-bottom` that used to do the
   stacking — margins would add to the gap, and `:last-child` stops meaning
   "bottom row" the moment there are two columns.

   The 220px minimum only produces a second column if the track is wide enough
   to hold two; that arithmetic spans two files and is asserted in
   tests/css.test.js rather than trusted. */
#internals-body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-4);
  align-items: start;
}
/* The model/capacity line describes the whole bin, so it spans the row rather
   than becoming a cell beside the first group. */
.internals__model { grid-column: 1 / -1; }

.stage {
  grid-area: stage;
  position: relative;
  overflow: hidden;
  background: linear-gradient(var(--stage-sky), var(--surface-0));
}
.stage canvas { display: block; width: 100%; height: 100%; }

.actions {
  grid-area: actions;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--surface-1);
  border-left: 1px solid var(--surface-2);
  overflow-y: auto;
}
/* Icon + label on one row. `> button` rather than a class because these buttons
   carry only `data-action`; the `.actions__btn--warn/--alert` modifiers that
   markFillLevel() toggles have never had a base class to sit on. */
.actions > button { display: flex; align-items: center; gap: var(--space-2); }
.actions__slider { display: grid; gap: var(--space-1); color: var(--ink-dim); font-size: var(--text-sm); }
.actions__slider-head { display: flex; align-items: center; gap: var(--space-2); }

/* The clock control, matching the HUD it sits opposite: a tracked micro-label
   and a segmented ladder of multipliers. */
.speed {
  grid-area: speed;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-1);
  border-top: 1px solid var(--surface-2);
  color: var(--ink-dim);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}
.speed__buttons { display: flex; gap: var(--space-1); }
/* The multipliers are numbers, not copy: they opt back out of the strip's
   uppercasing and tracking, and take tabular figures so 0.25x/0.5x/1x line up
   as a ladder rather than drifting with their digit widths. */
.speed__buttons button {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  text-transform: none;
  letter-spacing: normal;
}

.actions__feedback {
  margin: var(--space-1) 0 0;
  padding: var(--space-2);
  min-height: 1em;
  font-size: var(--text-sm);
  color: var(--ink);
  background: var(--surface-2);
  border-radius: var(--radius);
}
.actions__feedback:empty { display: none; }
.actions__feedback--error { color: var(--state-alert-ink); }

.speed__paused {
  color: var(--state-alert-ink);
  font-weight: 600;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  font-size: var(--text-sm);
}

/* The HUD status turns alert-colored whenever the farm is not "all good" (dead
   colony / full humus tray / full leachate tank), so the condition reads at a
   glance rather than as one word among five. The id keeps this ahead of the
   `.hud__item span:last-child` color rule without !important. */
#hud-status.hud__status--alert { color: var(--state-alert-ink); font-weight: 700; }
