/* ==========================================================================================
   MERGE MC — THE SHARED UI TOOLKIT.  ** DRAFT / DEMO CUT, rev 3, 2026-09-01 **

   This is the file docs/WEB_UI_TOOLKIT.md proposes as web/ui/ui.css. It lives under
   web/ui-demo/ so that NOTHING ON THE LIVE SITE LINKS IT. The wiki, /worth, the store and the
   home page are untouched; this is a thing to look at, not a thing that ships.

   REV 3, at Kelp's direction: THE WHOLE THING IS BIGGER, and it is bigger from one place.
   Rev 2 had twenty-three hardcoded font sizes and the smallest was .36rem - under six pixels.
   That is not a toolkit, it is twenty-three separate decisions, and it is why the text was not
   legible. There is now a TYPE SCALE, every size is drawn from it, and the whole UI scales from
   two numbers at the top: --ui-root (text) and --ui-scale (the chest furniture).

   Rev 2 added the chest slot and the animated glint. Rev 1 was the extraction from wiki.css.
   ========================================================================================== */

/* ---------------------------------------------------------------------------- THE SCALE
   Press Start 2P is a 5x7 pixel face. It has NO hinting and no small-size design: below about
   11px the strokes land between device pixels and it turns to mush, and no amount of
   antialiasing gets it back. So 11.2px (--fs-2xs) is the floor for pixel text, and it is used
   only for stack counts and tier numerals - things you glance at, never read.

   Prose is a different face (Rubik) with a proper hinted design, so its floor is lower.

   TO SCALE THE WHOLE UI: change --ui-root. Everything below is relative to it. */
:root{
  font-size:112.5%;              /* 18px. The browser default 16px made the pixel face too
                                    small at every step of the scale below. */
  --ui-scale:1;                  /* multiplies the chest furniture - slots, panels, gaps */
}

:root{
  --pixel:"Press Start 2P", ui-monospace, monospace;
  --sans:"Rubik", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* PIXEL TYPE SCALE. Nothing may use a size that is not on this ladder. */
  --fs-2xs:.62rem;    /* 11.2px - the floor. Stack counts, tier numerals. Glanced at. */
  --fs-xs:.72rem;     /* 13.0px - chips, table headers, tooltip material counts */
  --fs-sm:.86rem;     /* 15.5px - tooltip item names, chest titles */
  --fs-md:1.05rem;    /* 18.9px - section headings, small */
  --fs-lg:1.3rem;     /* 23.4px - section headings */
  --fs-xl:2rem;       /* 36.0px - page title */

  /* PROSE SCALE, in the sans face. */
  --fs-body:1rem;     /* 18px */
  --fs-lede:1.1rem;   /* 19.8px */
  --fs-note:.9rem;    /* 16.2px - captions, chest notes */

  --measure:1040px;       /* page column. Wider than rev 2's 900 because everything in it grew.
                             /worth overrides to 1240px; a data table needing the width is the
                             only legitimate reason to touch this. */
  --measure-text:62ch;

  --slot-cell-n:calc(64 * var(--ui-scale));    /* unitless px count; see .slot */
  --slot-cell:calc(var(--slot-cell-n) * 1px);    /* was 48. A 300px render inside 48px was the
                                                  other half of the legibility problem. */
  --slot-inline-n:calc(42 * var(--ui-scale));
  --slot-inline:calc(var(--slot-inline-n) * 1px);
  --radius:9px;
  --gap:18px;
}

/* ---------------------------------------------------------------------------- THEME TOKENS */
:root{
  --bg:#0a0d0a; --ink:#eaf2e6; --muted:#9db197;
  --grass:#4ade5f; --grass-dim:#2fa347; --green:#22b34d; --green-dim:#188038;
  --line:rgba(255,255,255,.18);
  --card:rgba(10,13,10,.55);
  --accent:var(--grass);
  --surface:#29332d; --surface-hover:#344139;
  --surface-ink:#f0f4ef; --surface-muted:#cbd5cd;
  --nav-height:73px;

  /* THE CHEST GUI. Vanilla's real values, not approximations: a #C6C6C6 panel, #8B8B8B slots,
     and a two-tone bevel that is #373737 top-left and pure white bottom-right. A slot that is
     merely "light grey" reads as a web control; a slot that is these five values reads as the
     game. */
  --slot:#8B8B8B;
  --slot-inset:#373737;
  --slot-edge:#FFFFFF;
  /* THE PANEL IS THE GAME'S OWN GREY. It was a step greyer at #B9B9B9, which is a colour the game
     does not contain; #C6C6C6 is the value in every container texture on disk - including
     crafting_table.png, whose face now matches the chest it is drawn on rather than sitting a
     shade off it. */
  --panel:#C6C6C6;
  --panel-lit:#FFFFFF;
  --panel-dark:#4E4E4E;
  --panel-ink:#3A3A3A;
  /* The line the game draws OUTSIDE the bevel. Every vanilla GUI has it, and without it a light
     panel on the portal has no edge at all wherever a bright star sits behind the white bevel. */
  --panel-line:#1c1c1c;

  /* THE WELL - a recess cut into the panel, and the one grey that did NOT move with it. At
     #A8A8A8 under the old #B9B9B9 panel a well was seventeen steps down and barely read as one;
     against #C6C6C6 the same value is a recess you can see. */
  --well:#A8A8A8;
  --well-dark:#7a7a7a;      /* inverted bevel: dark top-left */
  --well-lit:#d8d8d8;       /* light bottom-right */

  /* A BUTTON FACE IS THE PANEL'S OWN MATERIAL, so these two move with it - the same ten steps
     below the panel, and the same five above it on hover, that #AFAFAF/#BEBEBE were under
     #B9B9B9. Slots (#8B8B8B) and pressed states stay where the game puts them. */
  --btn:#BCBCBC;
  --btn-hover:#CBCBCB;

  /* TIER RAMP. aqua -> purple -> gold, the order players already read as "better", and the same
     ramp the plugin uses (MachineTier.colour is AQUA / LIGHT_PURPLE / GOLD). Colour is never
     the only signal: every tier also carries a numeral and a word. */
  --t1:#9db197; --t2:#00b7d6; --t3:#8b2fd6; --t4:#d98800;
  --soon:#e0a13f;
  --up:#4ade5f; --down:#ffa33d;
}

*{box-sizing:border-box}
html{scroll-behavior:smooth}
/* The green gradient is gone: the ground is the end portal, painted by .endportal behind
   everything. body stays transparent so that shows through. */
body{
  min-height:100vh; min-height:100svh; display:flex; flex-direction:column;
  margin:0; background:#05000c; color:var(--ink);
  font-family:var(--sans); font-size:var(--fs-body); line-height:1.65;
  -webkit-font-smoothing:antialiased;
}
body > .wrap{flex:1; width:100%}
.wrap{max-width:var(--measure); margin:0 auto; padding:26px 22px 80px}
.wrap.wide{--measure:1240px}
code{font-size:.94em; background:rgba(255,255,255,.07); padding:1px 5px; border-radius:4px}

/* The old inline .bar/.nav/.lnk header was removed in rev 6 - .topbar replaced it, and a
   toolkit with two header components is a toolkit with one too many. */

/* ---------------------------------------------------------------------------- §4.2 UNIFIED */
header.hero{margin-bottom:30px}
h1{font-family:var(--pixel); font-size:var(--fs-xl); line-height:1.5; margin:0 0 20px}
h1 .mc, .sectionhead h2 .mc{color:var(--accent)}
.lede{color:var(--muted); font-size:var(--fs-lede); max-width:var(--measure-text); margin:0}

.sectionhead{margin:58px 0 16px}
.sectionhead h2{font-family:var(--pixel); font-size:var(--fs-lg); line-height:1.5; margin:0 0 12px}
.sectionhead.sm h2{font-size:var(--fs-md)}
.sectionhead p{color:var(--muted); margin:0; max-width:var(--measure-text)}

/* ==========================================================================================
   THE ATOM — a vanilla chest slot.

   Built out of box-shadow rather than border so the bevel costs no layout: two hard inset
   shadows, dark top-left and white bottom-right, exactly as the container texture draws them.
   No border-radius anywhere in here on purpose - the GUI has none, and a 5px radius is the one
   change that makes the whole thing stop reading as Minecraft.
   ========================================================================================== */
.slot{
  --size-n:var(--slot-cell-n);          /* unitless: the slot's size in px */
  --size:calc(var(--size-n) * 1px);
  width:var(--size); height:var(--size); flex:0 0 var(--size);
  background:var(--slot);
  box-shadow:
    inset 3px 3px 0 0 var(--slot-inset),
    inset -3px -3px 0 0 var(--slot-edge);
  border-radius:0;
  display:inline-flex; align-items:center; justify-content:center;
  position:relative; vertical-align:middle;
  isolation:isolate;   /* so the glint's blend mode cannot reach the page behind the slot */
}
/* HOW BIG THE ART SITS IN THE SLOT - one number, both tiers.
   The art tier is a percentage of the slot and the tex tier used to be a fixed 16px times a
   hand-tuned zoom (3.0 here, 2.4 in the /worth grid, 1.9 inline, 1*--gs in the crafting table).
   Two scaling models for one atom: they agreed only where somebody had tuned them to agree, and
   the moment a tex item sat next to an art item - the stews - they visibly did not.
   Now the tex zoom is DERIVED from the slot size and the same fill fraction, so every context
   that sets --size gets both tiers at matching size for free and there is nothing left to tune. */
.slot{
  --art-fill:.74;
  /* Unitless throughout, or transform:scale() silently rejects it. */
}

/* ==========================================================================================
   THE ITEM SLOT — one rule for every picture of an item on the site.

   WHY THIS REPLACED ELEVEN RULES. Eleven places sized an item image and each did it its own way.
   Seven of them used a fixed 16px box plus transform:scale(), which silently assumes every
   source is a 16px sprite. That held while it was true. Then the wiki's 300px renders and
   render-blocks.py's 48px cubes arrived, and those seven broke while the four using percentages
   did not - which is why the block suppressor and the coop scaled wrong and the tier matrix
   beside them did not. One of the seven was worse: .rscell scaled by calc(var(--rs) * ...),
   and --rs is a LENGTH, so scale() rejected it and the image sat at natural size.

   THE SPLIT THAT FIXES IT. A slot decides HOW BIG ITS BOX IS. This rule decides HOW THE IMAGE
   FILLS THAT BOX, and does it in a way no source resolution can break:

     percentage sizing   74% of whatever the box is - identical for a 16px sprite, a 48px cube
                         and a 300px render, because a percentage has no opinion about pixels.
     object-fit: cover   scales to fill rather than cropping at natural size. For a square source
                         that is simply "fill the box".
     object-position:top which is what makes an ANIMATION STRIP work. Lava and sea lantern ship
                         as one tall column of frames; cover scales the width to fit and top
                         anchors it, so you see frame one at full size. The old code needed a
                         separate crop rule and a hand-tuned zoom to do the same job worse.
     transform: none     nothing here scales by transform, so nothing here can be broken by a
                         unit slipping into a scale().

   THE LIST IS GONE, AND THAT WAS THE BUG. This rule used to name seven selectors and its own
   comment said "ADD A NEW KIND OF SLOT by adding it to this list" - .rscell, .tblock, .fcell,
   .mcell and .ci were five item boxes that existed only because the primitive was unreachable from
   a browser. MC.slot (ui.js) made it reachable, so there is one box again and this rule names it
   once. A NEW KIND OF SLOT IS NOW A SIZE VARIANT, below - never a new selector here.
   ========================================================================================== */
:root{ --art-fill:.74 }

.slot > img{
  width:calc(var(--art-fill) * 100%);
  height:calc(var(--art-fill) * 100%);
  object-fit:cover;
  object-position:top center;
  image-rendering:pixelated;
  transform:none;
  position:relative;
  z-index:1;
}

/* The boxes that want the image edge to edge rather than inset: inside a GUI texture the drawn
   slot IS the frame, so an inset image would sit in a box within a box. */
.crafttable .slot > img,
.slot.gui > img,
.slot.chip > img{ --art-fill:1 }

/* Nav-tab and dropdown icons are inline chrome, not items: a picture on a button, with no tooltip,
   no tier, no glint and no stack count. They are the one thing slotcheck.py allows outside a slot,
   and they carry their own small box. */
.tab > img{ width:20px; height:20px; flex:0 0 20px; object-fit:contain; image-rendering:pixelated }

/* ------------------------------------------------------------------ THE SIZE VARIANTS
   Every context that needs a different box picks one of these by name. Each is ONE class that sets
   --size-n and nothing else; a caller that invents its own size is how the site ended up with two
   scaling models for one atom. Keep this list and SIZES in ui.js and slot.py in step - the parity
   check renders one fixture per variant, so a size added to only one half fails the build. */
.slot.inline{--size-n:var(--slot-inline-n)}                    /* 42 - beside a line of text */
.slot.big   {--size-n:calc(96 * var(--ui-scale))}              /* the store's tier item */
.slot.grid  {--size-n:calc(26 * var(--ui-scale)); --art-fill:.82}  /* the tool explorer's wall */
/* The demos' cells. .rscell was 34px with a 9px label, sitting directly under 64px slots carrying
   an 11px one, and the difference was visible from across the room - a thinner bevel and a smaller
   number on what is meant to be the same square. One size down from a chest slot, not three. */
.slot.demo  {--size-n:calc(56 * var(--ui-scale))}
.slot.chip  {--size-n:calc(20 * var(--ui-scale)); background:none; box-shadow:none}
/* A cell laid on a GUI texture, sized in the game's own pixels. --gui-scale is set by whichever
   texture it is lying on: the crafting table's --gs, the tooltip recipe's --ms, the furnace's
   --fs2. The texture draws the slot, so the atom drops its fill and its bevel. */
.slot.gui{
  --size-n:calc(16 * var(--gui-scale, 2));
  background:none; box-shadow:none;
}

/* A <button> that is a slot - the demos' lever and dust - is still a slot. */
button.slot{border:0; padding:0; margin:0; cursor:pointer; font:inherit; color:inherit}
button.slot:hover{background-color:#C5C5C5}

/* HOVER, the way the game does it: the slot lightens. It does not lift, and it does not glow. */
.slot.pick:hover, .slot.pick:focus-visible{background-color:#C5C5C5}


/* ------------------------------------------------------------------ THE ENCHANTMENT GLINT
   WAVY, NOT A SEARCHLIGHT. The first cut swept one hard bright band across the item, which read
   as a highlight sliding over glass rather than as an enchantment. Vanilla's is a soft
   REPEATING field of blotchy diagonal bands, low contrast, that seems to ripple because two
   copies of it move across each other at different angles and speeds.

   So: repeating gradients rather than one band, many soft stops rather than a hard edge, ~.3
   alpha rather than ~.9, and the two layers set 34 degrees apart. That angle difference does
   the waving - where the two sets of bands cross they reinforce and where they oppose they
   cancel, and the moire between them drifts as they scroll. Neither layer alone looks wavy.

   MASKED TO THE ITEM by its own PNG, handed in as --art. That is why the sheen runs over the
   hopper's metal and not over the empty corners of the square, and it is what a full-square
   gradient gets wrong. mask-size tracks .art img above; if that 74% moves, move it here too. */
/* THE ART ITSELF, for a slot that carries no <img>.

   WHY THIS RULE HAD TO EXIST. A slot paints its picture one of two ways: an <img> child, sized by
   the primitive above, or - where the markup wants an empty box it can also glint - the --art
   custom property. Only the second half was ever written. `--art` appeared exactly once in this
   file, as the glint's mask-image, so a `.slot.art` with no child rendered its grey background and
   a shimmer in the SHAPE of the item, and no item. Thirty slots across the site looked like
   missing images because, literally, nothing drew them.

   The geometry is deliberately identical to the mask's - same calc, same centring, same no-repeat -
   so the glint lines up with the art it is supposed to be glinting on. */
.slot.art{
  background-image:var(--art);
  background-size:calc(var(--art-fill) * 100%) calc(var(--art-fill) * 100%);
  background-position:center;
  background-repeat:no-repeat;
  image-rendering:pixelated;
}

.slot.glint::before,
.slot.glint::after{
  content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
  -webkit-mask-image:var(--art); mask-image:var(--art);
  -webkit-mask-size:calc(var(--art-fill) * 100%) calc(var(--art-fill) * 100%);
  mask-size:calc(var(--art-fill) * 100%) calc(var(--art-fill) * 100%);
  -webkit-mask-position:center;  mask-position:center;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  mix-blend-mode:screen;
}
.slot.glint::before{
  background:repeating-linear-gradient(112deg,
    rgba(150,60,225,0)    0px,
    rgba(150,60,225,.05)  9px,
    rgba(178,108,248,.22) 17px,
    rgba(214,170,255,.30) 22px,
    rgba(178,108,248,.22) 27px,
    rgba(150,60,225,.05)  35px,
    rgba(150,60,225,0)    49px);
  background-size:180% 180%;
  animation:glint-a 7.5s linear infinite;
}
.slot.glint::after{
  background:repeating-linear-gradient(78deg,
    rgba(126,40,200,0)   0px,
    rgba(126,40,200,.06) 12px,
    rgba(163,86,236,.20) 21px,
    rgba(126,40,200,.06) 31px,
    rgba(126,40,200,0)   44px);
  background-size:220% 220%;
  animation:glint-b 11s linear infinite;
  opacity:.8;
}
@keyframes glint-a{ from{background-position:0% 0%}     to{background-position:-180% 180%} }
@keyframes glint-b{ from{background-position:100% 100%} to{background-position:320% -80%} }

/* Motion is decoration here, never information: the item is still readable and its name is
   still in the tooltip and the aria-label with the sheen held still. */
@media (prefers-reduced-motion: reduce){
  .slot.glint::before, .slot.glint::after{animation:none}
  .slot.glint::before{background-position:50% 50%}
  .slot.glint::after{opacity:.5; background-position:50% 50%}
}

/* TIER RING + NUMERAL. The ring sits inside the slot's own edge so it never eats the bevel. */
.slot.t2{outline:4px solid var(--t2); outline-offset:-4px}
.slot.t3{outline:4px solid var(--t3); outline-offset:-4px}
.slot.t4{outline:4px solid var(--t4); outline-offset:-4px}
.slot .rank{
  position:absolute; right:-3px; top:-4px; z-index:3;
  font-family:var(--pixel); font-size:var(--fs-2xs); line-height:1;
  padding:3px 4px; color:#fff;
  text-shadow:1px 1px 0 rgba(0,0,0,.65);
}
.slot.t2 .rank{background:var(--t2)}
.slot.t3 .rank{background:var(--t3)}
.slot.t4 .rank{background:var(--t4)}

/* COUNT, bottom-right, drawn the way the game draws a stack size. */
.slot .qty,
.slot .badge{
  position:absolute; right:2px; bottom:0; z-index:3;
  font-family:var(--pixel); font-size:var(--fs-2xs); font-weight:400; color:#fff;
  text-shadow:2px 2px 0 rgba(0,0,0,.75);
}
/* A BADGE is a demo's reading - ON, OFF, 15, DIG - drawn where the game draws a stack count,
   because that is where the eye already goes for "what does this square say". It replaces the
   bare <b> the four demos each styled for themselves; the strip's label used to be .5rem while
   the identical slots above it were at the 11px floor, and the two visibly disagreed. */
.slot .badge{white-space:nowrap}

/* SPRITE-LESS. The manifest is the authority on which pictures exist (see ui.js), so a slot knows
   at the moment it is written that it has none: banners, mob heads, the copper golem statue and
   the chests. It keeps its shape and shows two letters. */
.slot .letters{
  position:absolute; inset:0; z-index:0; display:none;
  align-items:center; justify-content:center;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:var(--panel-ink);
}
.slot.noart .letters{display:flex}

/* NOT-LIVE DOT. Mark only the exception, and say what unmarked means in the legend. */
.slot.notlive::after{
  content:""; position:absolute; left:-5px; bottom:-5px; z-index:4;
  width:13px; height:13px;
  background:var(--soon); box-shadow:0 0 0 3px var(--panel);
  -webkit-mask-image:none; mask-image:none; mix-blend-mode:normal; animation:none;
}

/* ------------------------------------------------------------------ THE TOOLTIP
   Vanilla's item tooltip: a near-black box with a two-tone purple border, the name in white and
   the extra lines under it in grey. */
.slot[data-tip]{cursor:pointer}
.slot[data-tip] .tip{
  display:none;
  position:absolute; bottom:calc(100% + 11px); left:50%; transform:translateX(-50%);
  font-family:var(--pixel); line-height:1.7; letter-spacing:.5px;
  padding:11px 13px; z-index:20;
  background:#100010f2; color:#fff;
  text-shadow:2px 2px 0 rgba(0,0,0,.8);
  border:3px solid #2d0a63;
  box-shadow:0 0 0 3px #100010f2, inset 0 0 0 2px #5a189a55;
  opacity:0; visibility:hidden; transition:opacity .1s ease;
  text-align:left; white-space:nowrap;
}
.slot[data-tip]:focus-visible .tip,
.slot[data-tip].tipped .tip{display:block; opacity:1; visibility:visible}
@media(hover:hover){.slot[data-tip]:hover .tip{display:block; opacity:1; visibility:visible}}
.slot[data-tip].tip-dismissed .tip{display:none}
.slot[data-tip]:hover, .slot[data-tip]:focus-visible, .slot[data-tip].tipped{z-index:19}
.slot[data-tip].tipL .tip{left:0; transform:none}
.slot[data-tip].tipR .tip{left:auto; right:0; transform:none}
/* Flipped under the item, for a slot too near the top of the window to draw above it. */
.slot[data-tip].tipB .tip{bottom:auto; top:calc(100% + 11px)}

/* THE COST ROLL-UP, under the name. Vanilla puts an item's extra lines under its name in grey,
   so this does too. Each row is an icon and a count, because "625" beside an iron ingot is read
   faster than the words "625 Iron Ingot" and survives not knowing English. */
.tip .tipname{display:block; color:#fff; font-size:var(--fs-sm); margin-bottom:6px}
.tip .tipsub{display:block; color:#8a8a8a; font-size:var(--fs-2xs); margin-bottom:9px}
.tip .cost{display:flex; flex-wrap:wrap; gap:8px 14px; max-width:340px; white-space:normal}
/* .ci was a sixth item box: a picture, its own sizing rule, and a count. It is a ROW now - the
   picture is a chip-sized slot like every other picture on the site, and the count sits beside it,
   because a stack count drawn over a 20px icon cannot be read. */
.costrow{display:inline-flex; align-items:center; gap:6px; color:#c8c8c8; font-size:var(--fs-xs)}
.costrow i{font-style:normal; color:#fff}

/* ==========================================================================================
   THE CHEST PANEL — the container the wiki index becomes.
   ========================================================================================== */
/* FOUR THINGS MAKE THIS A GUI RATHER THAN A PAPER CUTOUT, and each answers something the flat
   slab got wrong against the end portal behind it:

     the outline    a 3px #1c1c1c line OUTSIDE the bevel, the way every vanilla container draws
                    one. The white bevel alone dissolves wherever a bright star sits behind it,
                    and a panel with no edge reads as a cutout.
     the halo       the game darkens the world behind an open inventory. Big blur, small offset:
                    it is not a card shadow lifting the panel off a page, it is the stars
                    directly behind the panel going quiet.
     the top tint   the section colour, 35% into the TOP bevel line only. Each section then feels
                    different on the panel itself rather than only in the title underline.
     the grey       --panel, which is the game's #C6C6C6.

   The two inset bevels are split top/left so the tint can land on the top edge alone; the top is
   listed first, so it owns the corner. */
.chest{
  background:var(--panel);
  border:1px solid var(--panel-line);
  border-top:3px solid color-mix(in srgb, var(--section) 50%, var(--panel-lit));
  box-shadow:0 10px 32px rgba(0,0,0,.24);
  padding:26px;
  margin:0 0 8px;
}
/* THE TITLE IS THE GAME'S "Crafting" LABEL: left, on the panel, at the size a heading wants.
   It was 15.5px and centred, which reads as a caption over a gallery rather than as the name of
   the container you are looking into. The tint bar under it moves to the text's left edge. */
.chest .chesttitle{
  font-family:var(--pixel); font-size:var(--fs-md); color:var(--panel-ink);
  margin:4px 0 16px; letter-spacing:.5px; line-height:1.5;
}

/* ==========================================================================================
   THE WELL — a recess cut into the panel, and the panel's only other surface.

   .itemgrid and .rsrow each drew this from scratch: the same #A8A8A8 and the same inverted
   bevel, written twice. Everything else that held content - the tier matrix, the icon walls, the
   stew rows, the recipe viewer, the movers, the economy strip, the hotbar, the tool wall - sat
   BARE on the panel, so a chest was a slab with things loose on it. In the game a panel is never
   bare: content sits in a recess. One rule now, and a zone joins it by being named here.
   ========================================================================================== */
.well,
.itemgrid, .rsrow, .toolgrid,
.tiertables, .iconwall, .stewtiles, .recipeview, .movers, .econ, .hotbar,
.hometiles, .steps{
  background:var(--well);
  box-shadow:none;
  padding:10px;
}
.chest .chestnote{
  font-family:var(--sans); font-size:var(--fs-note); color:#4a4a4a;
  margin:16px 0 0; max-width:70ch;
}
.iconwall{display:flex; flex-wrap:wrap; gap:5px; margin:0; list-style:none}
.iconwall.grouped{gap:20px}
.iconwall .group{display:flex; gap:5px; flex-wrap:wrap}
.iconwall a{text-decoration:none; display:inline-flex; line-height:0}
.walllegend{
  display:flex; gap:22px; flex-wrap:wrap; align-items:center;
  margin:16px 0 0; color:var(--muted); font-size:var(--fs-note);
}
.walllegend b{color:var(--ink); font-weight:700}
.walllegend .dot{
  display:inline-block; width:13px; height:13px;
  background:var(--soon); margin-right:8px; vertical-align:middle;
}

/* ---------------------------------------------------------------------------- CHIPS */
.tag{
  display:inline-block; font-family:var(--pixel); font-size:var(--fs-xs);
  letter-spacing:1px; text-transform:uppercase; padding:7px 11px; border-radius:6px;
  color:#08130a; background:var(--accent);
}
.tag.t2{background:var(--t2); color:#fff} .tag.t3{background:var(--t3); color:#fff} .tag.t4{background:var(--t4); color:#fff}
.marker{
  display:inline-block; font-family:var(--pixel); font-size:var(--fs-2xs);
  letter-spacing:1px; text-transform:uppercase; padding:5px 8px; border-radius:5px;
  vertical-align:middle; margin-left:8px; white-space:nowrap;
  background:transparent; color:var(--muted); border:2px solid var(--line);
}
.marker.soon{color:var(--soon); border-color:var(--soon); border-style:dashed}
.marker.down{color:var(--down); border-color:var(--down)}
.marker.up{color:var(--up); border-color:var(--up)}

/* ---------------------------------------------------------------------------- CARD + TABLE */
.card{
  background:var(--card); border:2px solid var(--line); border-radius:14px;
  padding:22px; display:flex; flex-direction:column; gap:14px;
}
.cardgrid{display:grid; grid-template-columns:repeat(auto-fit,minmax(300px,1fr)); gap:var(--gap)}
.scrollx{overflow-x:auto}
table.data{width:100%; border-collapse:collapse; font-size:var(--fs-note)}
table.data th, table.data td{padding:13px 14px; text-align:left; border-bottom:1px solid rgba(255,255,255,.08)}
table.data thead th{font-family:var(--pixel); font-size:var(--fs-2xs); letter-spacing:1px; text-transform:uppercase; color:var(--muted)}
.namecell{display:flex; align-items:center; gap:12px}

/* ---------------------------------------------------------------------------- .facts */
.facts{list-style:none; margin:0; padding:0; display:grid; gap:10px}
.facts li{display:flex; gap:12px; font-size:var(--fs-note)}
.facts b{flex:0 0 120px; color:var(--accent)}
.factgrid{
  list-style:none; margin:0; padding:0;
  display:grid; grid-template-columns:1fr 1fr; gap:1px;
  background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.08);
  border-radius:12px; overflow:hidden;
}
.factgrid li{background:#0c110c; padding:16px 18px; font-size:var(--fs-note)}

/* ==========================================================================================
   THE STORE THEME — decision 1, 2026-08-31.
   Same markup, same primitives, DIFFERENT TOKEN VALUES. Nothing here redefines a component,
   and the chest slot needs no override at all: it was already the game's.
   ========================================================================================== */
[data-theme="gui"]{
  --bg:#16161A; --ink:#FFFFFF; --muted:#AAAAAA;
  --line:#4A4A52;
  --card:#2A2A31;
  --accent:#FFAA00;                 /* the REAL gold */
  --green:#55FF55; --green-dim:#2f8f2f; --grass:#55FF55; --grass-dim:#2f8f2f;
  --radius:0px;
  --sans:"Press Start 2P", ui-monospace, monospace;
  background:var(--bg);
  background-image:
    repeating-linear-gradient(0deg,  rgba(255,255,255,.016) 0 16px, transparent 16px 32px),
    repeating-linear-gradient(90deg, rgba(255,255,255,.016) 0 16px, transparent 16px 32px);
}
/* The GUI face is far wider than Rubik at the same size, so prose in this theme steps DOWN the
   scale rather than keeping the site's body size. */
[data-theme="gui"] .lede,
[data-theme="gui"] .sectionhead p,
[data-theme="gui"] .card p{font-size:var(--fs-xs); line-height:2}
[data-theme="gui"] .card{border-width:4px; border-color:#43434D; border-radius:0}
[data-theme="gui"] .lnk{border-radius:0; font-size:var(--fs-2xs)}

/* ---------------------------------------------------------------------------- SMALL SCREENS
   The scale steps down as a whole rather than per-element, which is the point of having one. */
@media (max-width:820px){
  :root{--ui-scale:.85}
  .factgrid{grid-template-columns:1fr}
  .iconwall.grouped{gap:12px}
}
@media (max-width:520px){
  :root{font-size:106.25%; --ui-scale:.75}
  .tip .cost{max-width:210px}
}

/* ---------------------------------------------------------------------------- NAMED GROUPS
   A run of squares with a gap after it is a layout; a run of squares with a NAME over it is an
   organisation. The label is the panel's own ink at the smallest pixel size, so it reads as a
   part of the chest rather than as page furniture sitting on top of it. */
.iconwall .group{display:flex; flex-direction:column; gap:7px}
.iconwall .groupslots{display:flex; gap:5px; flex-wrap:wrap}
.grouplabel{
  font-family:var(--pixel); font-size:var(--fs-2xs); letter-spacing:.5px;
  color:#5c5c5c; text-transform:uppercase; line-height:1;
}

/* ==========================================================================================
   THE SITE NAV — rev 6. Its own bar, full bleed, owning the top of the window.

   Rev 5 put the tabs inline at the top of the page column, where they sat among the old header
   text and read as page content. They are not page content: they are the frame. So the strip is
   now a band of its own that spans the viewport, sticks to the top, and the page hangs below it.

   The tabs are the creative inventory's. The ACTIVE tab is lifted and joined to the band, which
   is what says "you are inside this one" rather than "this one is highlighted".
   ========================================================================================== */
.topbar{position:sticky; top:0; z-index:60; background:#151c19; border-bottom:1px solid #36453c}
.topbar-in{max-width:1240px; margin:0 auto; padding:0 22px; height:72px;
  display:flex; align-items:center; gap:28px}
.topbar .brand{flex-shrink:0; font-family:var(--pixel); font-size:var(--fs-sm);
  text-decoration:none; color:#fff; white-space:nowrap}
.topbar .brand .mc{color:var(--accent)}
.tabs{display:flex; align-items:center; gap:6px; margin-left:auto}
.tab{display:flex; align-items:center; justify-content:center; gap:9px; position:relative;
  padding:12px 16px; min-height:46px; color:#c6d1c9; text-decoration:none;
  font-family:var(--sans); font-size:16px; font-weight:600; border-bottom:3px solid transparent}
.tab:hover{color:#fff; background:#26352c}
.tab[aria-current="page"]{color:#fff; background:#26352c; border-bottom-color:var(--section)}
.navjoin{display:flex; align-items:center; gap:12px; flex-shrink:0; min-height:44px;
  border:1px solid #4c6c55; padding:9px 12px; background:#243a2b; color:#edfaee;
  cursor:pointer; font-family:var(--sans); font-size:14px}
.navjoin:hover{background:#304d38}.navjoin .footcopy{font-size:13px; color:#aee9b9}
@media(max-width:1050px){.navjoin{display:none}}
@media(max-width:620px){
  :root{--nav-height:99px}
  .topbar-in{height:98px; padding:10px 14px 0; display:grid; grid-template-columns:minmax(0,1fr);
    grid-template-rows:24px 54px; gap:0}
  .topbar .brand{font-size:13px; line-height:24px}
  .tabs{display:grid; grid-template-columns:1fr .9fr 1fr 1.3fr 1fr; gap:4px; margin:0; width:100%}
  .tab{padding:9px 4px; gap:6px; min-width:0; font-size:13px}
  .tab img{width:20px; height:20px}
}
@media(max-width:420px){.tab{font-size:12px;gap:4px}.tab > img{display:none}}
@media(min-width:621px) and (max-width:800px){.topbar-in{gap:16px}.tab{padding:12px 9px;gap:6px;font-size:14px}}

/* ==========================================================================================
   THE END PORTAL GROUND — rev 13. One canvas; the drawing lives in portal.js.

   WHAT THIS USED TO BE, AND WHY IT IS NOT ANY MORE. Rev 12 stacked fourteen elements, each 170%
   of the viewport, each with mix-blend-mode and will-change:transform. That is fourteen
   compositor layers permanently resident on the GPU behind every page; when their texture memory
   does not fit, the compositor evicts and re-uploads, and THAT was the flickering on the wiki.
   It was never a tuning problem.

   It also could not rotate. A CSS tiled background scales and translates and stops there, so
   every pass was an axis-aligned copy of the same star sheet and the field read as flat repeats
   rather than depth. The real thing gives each pass its own rotation, which is precisely what
   stops the passes looking like one picture drawn thirteen times.

   A canvas is ONE layer and a canvas transform rotates, so both problems have the same answer.
   Everything about how the passes are built - scale, rotation, offset, per-pass colour multiply,
   additive blend, depth attenuation - is documented at the top of portal.js.

   The CSS that remains is a dark ground, so that a page with JavaScript off or a texture that
   fails to load is still the void rather than a white sheet.
   ========================================================================================== */
.endportal{
  position:fixed; inset:0; z-index:-1; overflow:hidden;
  background:#02060f;
  contain:strict;          /* nothing in here affects layout or paint anywhere else */
}
.endportal canvas{
  display:block; width:100%; height:100%;
  opacity:.45;
  image-rendering:pixelated;
}

/* ---------------------------------------------------------------------------- RECIPE ROW
   The crafting grid and its result. Three columns of slots, an arrow, and the output - the
   game's own arrangement, built from the same .slot as everything else. */
.craftrow{display:flex; align-items:center; gap:18px; flex-wrap:wrap}
.craftgrid{display:grid; grid-template-columns:repeat(3, auto); gap:5px}
.craftarrow{font-family:var(--pixel); font-size:var(--fs-md); color:var(--panel-ink)}

/* ==========================================================================================
   THE TIER TABLE — rev 8.

   Six families x three tiers was drawn as six loose runs of three, which is a LIST of ladders
   and makes the reader climb each one separately. It is really a matrix: family down the side,
   tier across the top. Laid out that way a whole column is one tier, so "what does Mega cost
   me across the board" is a glance rather than six glances, and the ladders line up.
   ========================================================================================== */
.tiertable{
  display:grid;
  grid-template-columns:repeat(3, minmax(0, 1fr));
  gap:6px 14px;
  align-items:center;
}
.tiertable .th{
  font-family:var(--pixel); font-size:var(--fs-2xs); letter-spacing:.5px;
  text-transform:uppercase; color:#fff; padding:6px 9px; text-align:center;
}
.tiertable .th.t2{background:var(--t2)}
.tiertable .th.t3{background:var(--t3)}
.tiertable .th.t4{background:var(--t4)}
/* The family name is its own line, spanning the whole grid and centred over the three machines
   it names. It used to be a fourth COLUMN to the left of them, which is why the trio never looked
   centred: the label ate a share of the width and pushed the icons right of their tier headings. */
.tiertable .fam{
  grid-column:1 / -1;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#3A3A3A;
  text-align:center; white-space:nowrap;
  margin:14px 0 2px; letter-spacing:.5px;
}
.tiertable .cell{display:flex; justify-content:center}
/* A hairline under each family row, so the eye tracks across a long row without drifting. */
.tiertable .rule{grid-column:1 / -1; height:2px; background:rgba(0,0,0,.10)}

@media (max-width:640px){
  .tiertable{grid-template-columns:repeat(3, auto); gap:5px 8px}
  .tiertable .fam{font-size:var(--fs-2xs); white-space:normal; max-width:9ch; line-height:1.4}
}

/* ==========================================================================================
   THE CRAFTING TABLE — the game's own GUI texture, with real slots laid on it.

   textures/crafting_table.png is the container GUI from the client jar, pulled the same way the
   item sprites and the portal textures were. The GUI occupies the top-left 176x166 of a 256x256
   sheet; it draws all 166 rows of it - the crafting grid, the arrow, the result and the player inventory
   underneath. Rev 17 cropped to the top 82 rows, which is the crafting area alone and is a squat
   176x82 box; the full GUI is 176x166, near enough square, which is what "grow its height to
   match the width" asks for. The inventory slots draw empty, which is what a crafting table
   looks like when you are reading a recipe rather than standing at one.

   THE COORDINATES ARE MEASURED, not remembered. Scanning the sheet for slot-grey (#8b8b8b) puts
   the grid's three columns at x 29/47/65 and its three rows at y 16/34/52, each slot 18px pitch
   with a 16px interior one pixel in - so item cells sit at x 30/48/66 and y 17/35/53. The result
   box is 26x26 at x 119, y 30, so its 16px item sits at x 124, y 35.

   EVERYTHING IS SCALED BY ONE INTEGER, --gs. Integer only: at 3.5x a 16px sprite lands on half
   pixels and the whole thing goes soft, which is the one way to make pixel art look wrong.

   The cells are ordinary .slot elements with their chrome switched off - the texture is drawing
   the slot now - so the tooltips, the tier rings, the glint and the art tiers all still work.
   ========================================================================================== */
.crafttable{
  --gs:5;
  --gui-scale:var(--gs);        /* what .slot.gui sizes itself from */
  position:relative;
  width:calc(176px * var(--gs));
  height:calc(166px * var(--gs));
  max-width:100%;
  background-image:url(/ui/textures/crafting_table.png);
  background-size:calc(256px * var(--gs)) calc(256px * var(--gs));
  background-repeat:no-repeat;
  image-rendering:pixelated;
}
.crafttable .ctcell{position:absolute}
/* Inside the table the texture IS the slot, so the atom drops its own fill and bevel. */
.crafttable .slot{
  position:relative;
  --size-n:calc(16 * var(--gs));
  background:transparent;
  box-shadow:none;
}
.crafttable .slot.pick:hover{background-color:rgba(255,255,255,.28)}
.crafttable .slot .rank{right:-4px; top:-6px}
.crafttable .ctlabel{
  position:absolute; left:calc(8px * var(--gs)); top:calc(5px * var(--gs));
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#3f3f3f;
}
@media (max-width:1000px){ .crafttable{--gs:4} }
@media (max-width:760px){  .crafttable{--gs:3} }
@media (max-width:520px){  .crafttable{--gs:2} }

/* ==========================================================================================
   PANEL FADE-IN.

   The panels are heavy - a chest full of 300px renders, or fourteen hundred sprites - so they
   land in visible steps as the images decode. A short rise-and-fade covers that: the panel is
   already in place before its contents finish arriving, so what you see is one thing appearing
   rather than several arriving.

   `both` matters: without it the panel is fully visible for one frame before the animation runs,
   which is a flash rather than a fade. The stagger is small on purpose - it should read as one
   page settling, not as a sequence being performed.
   ========================================================================================== */
@keyframes panel-in{
  from{ opacity:0; transform:translateY(14px) }
  to  { opacity:1; transform:none }
}
@keyframes bar-in{
  from{ opacity:0; transform:translateY(-10px) }
  to  { opacity:1; transform:none }
}
/* FILL-MODE MATTERS MORE THAN IT LOOKS. `both` leaves the animation permanently in effect, and
   an animation of opacity/transform that is in effect CREATES A STACKING CONTEXT. Every panel
   therefore became its own context, so a tooltip's z-index only stacked inside its own panel and
   the next panel - a later sibling, painted wholesale afterwards - covered anything that hung
   below the first one's edge. With 300px crafting tooltips that is most of them, and the result
   is a tooltip sliced in half by the panel underneath.

   `backwards` gives the same fade: the from-state applies before the animation starts, so there
   is still no flash of a fully-visible panel, and once it finishes the element reverts to its
   ordinary styles - no animation in effect, no stacking context, tooltips free to paint over
   whatever follows them. */
.topbar{ animation:bar-in .34s ease backwards }
.wrap > .chest{ animation:panel-in .42s cubic-bezier(.2,.7,.3,1) backwards }

/* Belt and braces for the same problem: a hovered panel lifts above its later siblings, so even
   if something else in the page grows a stacking context, a tooltip still paints over the panel
   below it. */
.wrap > .chest{ position:relative; z-index:1 }
.wrap > .chest:hover, .wrap > .chest:focus-within{ z-index:40 }
.wrap > .chest:nth-of-type(1){ animation-delay:.04s }
.wrap > .chest:nth-of-type(2){ animation-delay:.10s }
.wrap > .chest:nth-of-type(3){ animation-delay:.16s }
.wrap > .chest:nth-of-type(4){ animation-delay:.22s }

/* Content that arrives from a fetch cannot be staggered in CSS - it is not there at parse time -
   so worth.js adds .ready once the grid is populated. */
.itemgrid{ opacity:0; transition:opacity .3s ease }
.itemgrid.ready{ opacity:1 }

@media (prefers-reduced-motion: reduce){
  .topbar, .wrap > .chest{ animation:none }
  .itemgrid{ opacity:1; transition:none }
}

/* ---------------------------------------------------------------------------- RATES
   Throughput, printed under the item it belongs to. Every number is derived from the plugin's
   own constants - see RATES in build.py for the arithmetic and where each constant lives. */
.rate{
  display:block; margin-top:7px; text-align:center;
  font-family:var(--pixel); font-size:var(--fs-2xs); line-height:1.5; color:#3A3A3A;
}
.rate b{color:#14506b; font-weight:400}
.tiertable .cell{flex-direction:column; align-items:center}

/* THE DRAWN MINI RECIPE IS GONE, 2026-09-03. The tooltip used to carry the whole recipe on a crop
   of crafting_table.png - nine positioned slots and a result - and every rule that sized it lived
   here. It was 180 KB of second copy across the page; the click viewer draws the same recipe
   bigger and can be linked to. What the tooltip keeps is .mtotal and the roll-up. */
/* The two counts a player actually wants off a recipe: what it eats, and what it gives back. */
.tip .mtotal{
  display:block; color:#c8c8c8; font-size:var(--fs-2xs); margin:2px 0 4px;
}
.tip .mtotal b{color:#fff}

/* ---------------------------------------------------------------------------- COMING SOON
   A chest whose contents do not exist yet. Marked on the panel, not only on the items, because
   a whole section being unbuilt is a different claim from one item in it being unbuilt. */
.chest.soon{
  background:#c6bfb0;
  box-shadow:
    inset 0 4px 0 0 #efe8d8,
    inset 4px 0 0 0 #efe8d8,
    inset -4px -4px 0 0 #6f6a5e,
    0 0 0 3px var(--panel-line),
    0 14px 48px rgba(0,0,0,.55);
}
.soonflag{
  font-family:var(--sans); font-size:var(--fs-note); font-weight:700;
  color:#7a4d00; background:#f5c265; padding:3px 9px; margin-left:10px; vertical-align:middle;
}
.chest.soon .slot{filter:saturate(.35)}

/* ---------------------------------------------------------------------------- LONG FORM
   The one place on the site with real prose. Everything else is items in slots; the placer and
   the breaker have a mechanic that a slot cannot carry, so this is a deliberate exception rather
   than the descriptions the rest of the page had removed. */
.longform{margin-top:20px; max-width:74ch}
.longform p{margin:0 0 14px; color:#2f2f2f; font-size:var(--fs-note); line-height:1.75}
.longform p:last-child{margin-bottom:0}
.longform b{color:#14506b}
.longform em{font-style:italic}

/* ==========================================================================================
   THE BREAKER RANGE DIAGRAM.

   Replaced the prose that used to sit here. The rule - distance = 16 - signal - runs backwards
   from what anyone expects, and a paragraph saying so is much less convincing than a lever you
   can flip. Everything drawn is derived from one integer, how many dust the signal crossed.
   ========================================================================================== */
/* .rscell is gone: every cell in every demo is a `demo`-sized .slot, so the strip has the same
   bevel, the same image rule and the same label size as the slots in the panel above it. What is
   left here is the STATE - which is all these classes ever should have been. */
.rsrow{
  display:flex; gap:2px; align-items:center; flex-wrap:nowrap;
  overflow-x:auto; padding:10px 4px 14px;
}
.rsdemo{margin-top:18px}

.rslever.on{outline:3px solid #ff5555; outline-offset:-3px}
.rsdust.live{background:#a06060}
.rsdust.live .badge{color:#ffd0d0}
.rsbreaker{outline:3px solid #3A3A3A; outline-offset:-3px}
.rsbreaker.live{outline-color:#ff5555}
.rsblock{background:#7d7d7d}
.rsblock.target{outline:3px solid #ffb43d; outline-offset:-3px; background:#9a8a6a}
.rsblock.target .badge{color:#ffe6b0}
@keyframes rs-swing{
  0%{transform:none} 40%{transform:translateX(-3px) scale(.9)} 100%{transform:none}
}
.rsblock.swing{animation:rs-swing .34s ease}

.rscontrols{display:flex; gap:10px; align-items:center; flex-wrap:wrap; margin-top:12px}
.rsbtn{
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#2b2b2b; background:var(--btn);
  border:0; cursor:pointer; padding:10px 14px;
  box-shadow: inset 2px 2px 0 0 #FFFFFF, inset -2px -2px 0 0 #6a6a6a;
}
.rsbtn:hover{background:var(--btn-hover)}
.rsbtn:active{box-shadow: inset 2px 2px 0 0 #5c5c5c, inset -2px -2px 0 0 #cfcfcf}
.rsread{
  font-family:var(--sans); font-size:var(--fs-note); color:#2f2f2f; margin:12px 0 0;
}
.rsread b{color:#14506b}
.rsrule{
  display:inline-block; margin-left:10px; padding:3px 8px;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#fff; background:#14506b;
}
.rshint{font-family:var(--sans); font-size:var(--fs-note); color:#4a4a4a; margin:10px 0 0}

/* ---------------------------------------------------------------------------- DROPDOWNS
   <details> panels, drawn as vanilla buttons that open. Used for the breaker's long tail: the
   tools it takes, what repairs it, and what each tool can actually mine. */
.drop{margin-top:10px; background:var(--btn); box-shadow: inset 2px 2px 0 0 #d8d8d8, inset -2px -2px 0 0 #7a7a7a}
.drop > summary{
  cursor:pointer; list-style:none; padding:13px 16px;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#2b2b2b;
}
.drop > summary::-webkit-details-marker{display:none}
.drop > summary::before{content:"\25B8  "; color:#5c5c5c}
.drop[open] > summary::before{content:"\25BE  "}
.drop > summary:hover{background:var(--btn-hover)}
.dropbody{padding:4px 16px 16px}
.dropbody p{margin:0 0 12px; font-size:var(--fs-note); color:#2f2f2f; line-height:1.7; max-width:70ch}
.dropbody p:last-child{margin-bottom:0}
.dropbody b{color:#14506b}

.toolpick{display:flex; gap:14px; flex-wrap:wrap; align-items:flex-end; margin-bottom:14px}
.toolpick label{display:flex; flex-direction:column; gap:6px}
.toolpick span{font-family:var(--pixel); font-size:var(--fs-2xs); color:#3A3A3A}
.toolpick select{
  font-family:var(--sans); font-size:var(--fs-note); color:#1c1c1c;
  background:var(--btn); border:0; padding:9px 12px; cursor:pointer;
  box-shadow: inset 2px 2px 0 0 #6a6a6a, inset -2px -2px 0 0 #FFFFFF;
}
.toolout{font-size:var(--fs-note); color:#2f2f2f; line-height:1.7; margin:0 0 12px; max-width:70ch}
.toolout b{color:#14506b}
.toolgrid{
  display:flex; flex-wrap:wrap; gap:3px; max-height:230px; overflow-y:auto;
  padding:6px;
}
/* .tblock is gone - the wall is 240 `grid`-sized slots, which is the same box the rest of the
   site draws an item in, one size down. */
.toolnote{font-size:var(--fs-note); color:#4a4a4a; margin:12px 0 0; max-width:70ch}
.toolnote b{color:#14506b}

/* ==========================================================================================
   THE AUTOMATION SUB-SECTIONS.

   This was a tab strip: one tab per block, and inside the first of them a second strip of two.
   Five demos then shared one panel, so the page showed a fifth of itself and gave no sign that
   the rest existed. They are stacked and headed now - the strip is gone, and with it the reason
   anyone needed to guess what was behind it.

   A rule, not a border: the panel is already a box, and a box inside a box inside the chest reads
   as clutter. The heading and the line above it are what separates one block from the next.
   ========================================================================================== */
.autosec{padding:26px 2px 2px}
.autosec + .autosec{margin-top:10px; border-top:3px solid rgba(0,0,0,.16)}
.autoname{
  font-family:var(--pixel); font-size:var(--fs-xs); color:var(--panel-ink);
  margin:0 0 16px; text-align:center; letter-spacing:.5px;
}
.autosub{margin-top:26px}
.autosubname{
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#4a4a4a;
  margin:0 0 14px; text-align:center; letter-spacing:.5px;
}

/* ---------------------------------------------------------------------------- demo scenes */
.rsrow.vert{flex-direction:column; align-items:center; overflow:visible; width:max-content; margin:0 auto}
.rsrow.dark{background:#4a4a52}
.rslamp{outline:3px solid #6a6a6a; outline-offset:-3px}
.rslamp.live{outline-color:#ffd166; background:#b8a05a}
.rslit{background:#a89a6a}
.rspopped{opacity:.45; outline:3px dashed #d06a6a; outline-offset:-3px}
.rsheld{outline:3px solid #3aa7ff; outline-offset:-3px}
.rsfall{outline:3px solid #9c9c9c; outline-offset:-3px}
.rsair{background:#6f6f6f; box-shadow:none}
.rsfired{outline:3px solid #ff5555; outline-offset:-3px; background:#9a6a6a}
.rscoop{outline:3px solid #7a5a2a; outline-offset:-3px}
.rscoop.live{outline-color:#4ade5f; background:#6a8a5a}
.rsmove{outline:3px solid #ffd166; outline-offset:-3px}

/* ---------------------------------------------------------------------------- centring
   Kelp: centre the special-item panels and their titles. The chests are galleries of items, and
   a gallery centres; the long-form prose under the breaker stays left-aligned, because centred
   paragraphs are hard to read. */
.chest .iconwall{justify-content:center}
.chest .rsread, .chest .rshint, .chest .toolout, .chest .toolnote{margin-left:auto; margin-right:auto}
.chest .rscontrols{justify-content:center}
.tiertable{justify-content:center; margin:0 auto}

/* ---------------------------------------------------------------------------- FURNACE GUI
   The breaker is a furnace, so its repair panel is drawn on the furnace's own GUI. Same method as
   the crafting table: crop the sheet to the machine, then place cells at measured coordinates.

   MEASURED, not remembered. Scanning furnace_gui.png for slot-grey puts the slot column at x
   55-72 and the result box at x 111-136, with slot rows spanning y 16-69 - the vanilla layout:
   input at 56,17, fuel at 56,53, result at 112,31. Cropping at x 54 / y 15 (one pixel out, to keep
   each slot's bevel) gives an 84 x 56 box with cells at 2,2 / 2,38 / 58,16. */
.furngui{
  --fs2:4;
  --gui-scale:var(--fs2);       /* what .slot.gui sizes itself from */
  position:relative;
  width:calc(84px * var(--fs2));
  height:calc(56px * var(--fs2));
  margin:0 auto;
  background-image:url(/ui/textures/furnace_gui.png);
  background-size:calc(256px * var(--fs2)) calc(256px * var(--fs2));
  background-position:calc(-54px * var(--fs2)) calc(-15px * var(--fs2));
  background-repeat:no-repeat;
  image-rendering:pixelated;
}
/* .fpos is a POSITION on the texture; the slot inside it is the item - the same split .ctcell and
   .slot use on the crafting table. .fcell was both at once, which is why it needed its own entry
   in the shared image rule. */
.fpos{
  position:absolute;
  width:calc(16px * var(--fs2)); height:calc(16px * var(--fs2));
  display:flex; align-items:center; justify-content:center;
}
/* The fuel slot is the one that repairs, so it is the one marked. */
.furngui .slot.fuel{outline:3px solid #4ade5f; outline-offset:-3px; background:rgba(74,222,95,.18)}
/* Vanilla's durability bar: a dark trough with a green fill that shortens as the tool wears. */
.furngui .slot .dura{
  position:absolute; left:12%; right:12%; bottom:10%; height:9%;
  background:#000; display:block; z-index:2;
}
.furngui .slot .dura b{display:block; height:100%; background:#4ade5f; transition:width .28s ease}
.rsdim{opacity:.5; outline:3px solid #4a4a52; outline-offset:-3px}
.rsdim b{color:#9aa}
@media (max-width:760px){ .furngui{--fs2:3} }


/* ---------------------------------------------------------------------------- ICON PICKERS
   A tool and a material are both things with a picture, so the picker shows pictures. The name
   stays on each button as title and aria-label, so it is still reachable by keyboard or reader. */
.picklab{font-family:var(--pixel); font-size:var(--fs-2xs); color:#3A3A3A; align-self:center}
.pickrow{display:flex; gap:4px; flex-wrap:wrap}
.pickbtn{
  width:44px; height:44px; padding:0; border:0; cursor:pointer;
  background:#8B8B8B;
  box-shadow: inset 2px 2px 0 0 #373737, inset -2px -2px 0 0 #FFFFFF;
  display:flex; align-items:center; justify-content:center;
}
.pickbtn:hover{background:#C5C5C5}
.pickbtn[aria-checked="true"]{background:var(--panel); outline:3px solid #4ade5f; outline-offset:-3px}

/* An enchantment has no item of its own to show, so this one picker stays words - the same
   button, widened, rather than a second control that behaves differently. */
.pickbtn.text{
  width:auto; height:44px; padding:0 14px;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#2b2b2b;
}

/* ---------------------------------------------------------------------------- ICON DROPDOWN
   Eighteen selection buttons on screen at once was the clutter. A dropdown shows the current
   choice and opens the rest, and every option stays a picture rather than reverting to a word. */
.icodrop{position:relative; display:flex; flex-direction:column; gap:6px}
.icodrop-btn{
  display:flex; align-items:center; gap:10px; cursor:pointer;
  font-family:var(--sans); font-size:var(--fs-note); color:#1c1c1c;
  background:var(--btn); border:0; padding:9px 12px; min-width:172px;
  box-shadow: inset 2px 2px 0 0 #FFFFFF, inset -2px -2px 0 0 #6a6a6a;
}
.icodrop-btn > img{width:26px; height:26px; flex:0 0 26px}
.icodrop-btn > span{flex:1 1 auto; text-align:left}
.icodrop-btn > i{font-style:normal; color:#4a4a4a}
.icodrop-btn:hover{background:var(--btn-hover)}
.icodrop-btn[aria-expanded="true"]{background:#8f8f8f; color:#fff;
  box-shadow: inset 2px 2px 0 0 #5c5c5c, inset -2px -2px 0 0 #cfcfcf}

.icodrop-menu{
  position:absolute; top:100%; left:0; z-index:30; min-width:100%;
  display:flex; flex-direction:column; gap:2px; padding:4px;
  background:var(--well);
  box-shadow: inset 2px 2px 0 0 var(--well-lit), inset -2px -2px 0 0 var(--well-dark), 0 6px 0 0 rgba(0,0,0,.25);
}
.icodrop-menu[hidden]{display:none}
.icodrop-opt{
  display:flex; align-items:center; gap:10px; cursor:pointer; white-space:nowrap;
  font-family:var(--sans); font-size:var(--fs-note); color:#1c1c1c;
  background:#8B8B8B; border:0; padding:8px 12px;
  box-shadow: inset 2px 2px 0 0 #373737, inset -2px -2px 0 0 #FFFFFF;
}
.icodrop-opt > img{width:24px; height:24px; flex:0 0 24px}
.icodrop-opt:hover{background:#C5C5C5}
.icodrop-opt[aria-selected="true"]{background:var(--panel); outline:3px solid #4ade5f; outline-offset:-3px}

/* ---------------------------------------------------------------------------- HOME
   The landing page was the last thing on the old look. It is three panels now: what the server
   is, where to go, and where else we are. */
.homelede{font-size:var(--fs-lede); color:#2f2f2f; margin:0 auto; max-width:60ch; text-align:center}
.homelede b{color:#14506b}
/* ==========================================================================================
   THE SERVER ADDRESS — the whole slab is the button.

   It replaced a line of static text that said play.mergemc.net, which does not exist: there is no
   play. host at all, and the SRV record _minecraft._tcp.mergemc.net is what carries the client to
   the tunnel and its port. Anyone who typed what the page showed got "can't resolve hostname".
   The address is the bare domain, so there is no port to render and nothing to explain.

   ONE BIG TARGET rather than a label plus a small copy button: the thing you want is the address,
   so the address is the thing you press. The glyph is an affordance, not a second control. */
.ipbtn{
  display:block; margin:22px auto 6px; cursor:pointer;
  font-family:var(--pixel); font-size:var(--fs-md); color:#14506b;
  background:var(--btn); border:0; border-radius:0;
  padding:18px 26px;
  box-shadow: inset 3px 3px 0 0 #FFFFFF, inset -3px -3px 0 0 #565656;
  display:flex; align-items:center; justify-content:center; gap:14px;
}
.ipbtn:hover{background:var(--btn-hover)}
.ipbtn:active{box-shadow: inset -3px -3px 0 0 #FFFFFF, inset 3px 3px 0 0 #565656}
.ipbtn .ipmark{color:#3f6b80; font-size:var(--fs-sm)}
.iphint{
  font-family:var(--sans); font-size:var(--fs-note); color:var(--panel-ink);
  text-align:center; margin:0 0 4px; opacity:.75;
}

/* Each letter carries its own --n, so the wobble is offset per character and never reads as the
   whole word moving as one block - which is what makes it look alive rather than animated. */
.ipch{display:inline-block; animation:ipjiggle 2.6s ease-in-out infinite;
      animation-delay:calc(var(--n) * .13s)}
@keyframes ipjiggle{
  0%,100%{ transform:translateY(0) rotate(0deg) }
  25%    { transform:translateY(-2px) rotate(-2.5deg) }
  50%    { transform:translateY(0) rotate(0deg) }
  75%    { transform:translateY(1.5px) rotate(2.5deg) }
}
/* On copy the letters punch upward in sequence and settle. Overshoot in the easing, so it lands
   like something dropped rather than something stopping. */
.ipbtn.copied .ipch{
  animation:ippop .44s cubic-bezier(.2,.9,.3,1.5) both;
  animation-delay:calc(var(--n) * .035s);
}
@keyframes ippop{
  0%  { transform:translateY(0) scale(1) }
  40% { transform:translateY(-11px) scale(1.16) }
  100%{ transform:translateY(0) scale(1) }
}

/* A slot lifts under the pointer, the way picking one up feels. NOT inside the crafting or furnace
   GUIs: there the slot is absolutely placed on a texture, so lifting it would peel the item off
   the grid it is meant to be sitting in. */
.slot.pick{transition:background-color .12s ease, transform .12s ease}
.slot.pick:hover, .slot.pick:focus-visible{transform:translateY(-3px)}
.crafttable .slot.pick:hover, .furngui .slot.pick:hover,
.tip .slot.pick:hover{transform:none}

/* Panels below the fold rise as they are scrolled to. A TRANSITION rather than an animation, and
   both classes are stripped on transitionend, because a transform left in effect creates a
   stacking context - which is the exact bug the fade-in comment below documents, where a panel
   became its own context and sliced the tooltip hanging out of the panel above it. */
.chest.offscreen{opacity:0; transform:translateY(18px)}
.chest.rising{transition:opacity .5s ease, transform .5s cubic-bezier(.2,.7,.3,1)}

/* Anyone who has asked the OS for less motion gets the layout and none of the movement. */
@media (prefers-reduced-motion: reduce){
  .ipch, .ipbtn.copied .ipch{animation:none}
  .slot.pick:hover, .slot.pick:focus-visible{transform:none}
  .chest.offscreen{opacity:1; transform:none}
}
.hometiles{display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:12px}
/* The wiki index has FOUR tiles, and auto-fit put three on a desktop row and one alone under
   them. Four sections read as two pairs; three and a widow reads as a mistake. */
.hometiles.four{grid-template-columns:repeat(2,1fr)}
@media (max-width:660px){ .hometiles.four{grid-template-columns:1fr} }
.hometile{
  display:flex; align-items:center; gap:16px; text-decoration:none; padding:14px;
  background:var(--surface); border:1px solid #425148;
  box-shadow:none;
}
.hometile:hover{background:var(--surface-hover); border-color:#7c9d85}
.hometext{display:flex; flex-direction:column; gap:5px}
.hometext b{font-family:var(--pixel); font-size:var(--fs-xs); color:var(--surface-ink); text-shadow:none; line-height:1.7}
.hometext span{font-family:var(--sans); font-size:var(--fs-note); color:var(--surface-muted)}
/* THE STEW ROWS. The same box as a home tile - it is the right shape for an icon beside two lines
   of text - but not an <a>, so no hover lift: these rows are not links and must not offer to be
   clicked. The effect line carries the numbers, which is the whole reason the panel is a list of
   rows rather than a bare wall of five identical bowls. */
.stewtiles{display:grid; grid-template-columns:repeat(auto-fit,minmax(320px,1fr)); gap:12px}
.stewtile{
  display:flex; align-items:center; gap:16px; padding:14px;
  background:var(--surface); border:1px solid #425148;
  box-shadow:none;
}
.stewtext{display:flex; flex-direction:column; gap:5px}
.stewtext b{font-family:var(--pixel); font-size:var(--fs-xs); color:var(--surface-ink); text-shadow:none}
.stewtext span{font-family:var(--sans); font-size:var(--fs-note); color:var(--surface-muted)}
.stewtext .cap{color:var(--surface-muted)}
.stewnote{font-family:var(--sans); font-size:var(--fs-note); color:var(--panel-ink); margin:16px 0 0; line-height:1.6}

.homelinks{display:flex; gap:12px; flex-wrap:wrap; justify-content:center}
.lnkbtn{
  font-family:var(--pixel); font-size:var(--fs-2xs); text-decoration:none;
  color:#2b2b2b; background:var(--btn); padding:14px 22px;
  box-shadow: inset 2px 2px 0 0 #FFFFFF, inset -2px -2px 0 0 #6a6a6a;
}
.lnkbtn:hover{background:var(--btn-hover)}

/* ---------------------------------------------------------------------------- panel spacing
   Panels are separate ideas. At 14px they read as one long column; at 44px each one is its own
   thing. Set here rather than per page, and never as an inline style - it was repeated on every
   panel and had to be changed in a dozen places. 44px left 44px of raw portal between panels and
   a page read as a stack of loose slabs; at 22px, with each panel outlined and haloed, they read
   as one GUI. */
.wrap > .chest + .chest,
.wrap > .tiergrid,
.tiergrid + .chest{margin-top:36px}

/* A square that content does not fit into stops being a square rather than hiding what is in it.
   aspect-ratio sets the preferred shape; min-height lets it grow if the perks need the room. */
.chest.tier{min-height:max-content; overflow:visible}

/* ==========================================================================================
   REV 35, 2026-09-02 — THE SITE STOPS ENDING ABRUPTLY, AND STOPS HIDING ITS NAMES.

   Four things, all from one review of the live pages at desktop and phone width:
     - every page ended after its last panel, and on a tall screen the lower two thirds of the
       window was empty portal. A footer band closes each page now.
     - names were hover-only almost everywhere. Slots that stand alone carry a caption.
     - the tier table stacked six families in one narrow column. Two columns from 900px.
     - the section tint portal.js already paints was invisible on the panels. A thin accent now
       carries it: the bar under a chest title and the stripe on the active tab.
   ========================================================================================== */

/* ---------------------------------------------------------------------------- SECTION TINT
   The spark colour of each section's portal palette (portal.js PALETTES). Carried on <body
   data-section>, because the top bar div is matched literally by stampnav and cannot take an
   attribute. Used only as an accent, never as text - on the grey panel none of the four would
   pass contrast as a word. */
:root{--section:#8fd4d8}
body[data-section="home"]{--section:#6a9ef8}
body[data-section="wiki"]{--section:#59aab2}
body[data-section="prices"]{--section:#a5f86a}
body[data-section="store"]{--section:#f86ab1}
.chest .chesttitle::after{
  content:""; display:block; width:56px; height:4px; margin:10px 0 0;
  background:var(--section); box-shadow:2px 2px 0 0 rgba(0,0,0,.25);
}

/* ---------------------------------------------------------------------------- THE FOOTER
   The same band as the top bar, closing the page instead of opening it. */
.sitefoot{
  margin-top:40px; flex-shrink:0; background:#151c19; color:#cfcfcf;
  border-top:4px solid #000; box-shadow:0 -4px 0 0 rgba(0,0,0,.45);
}
.footin{
  max-width:var(--measure); margin:0 auto; padding:26px 22px 34px;
  display:flex; align-items:center; justify-content:space-between; gap:18px 28px; flex-wrap:wrap;
}
.footleft{display:flex; align-items:center; gap:16px; flex-wrap:wrap}
.footbrand{font-family:var(--pixel); font-size:var(--fs-sm); color:#fff; text-decoration:none; white-space:nowrap}
.footbrand .mc{color:var(--accent)}
.footip{
  display:inline-flex; align-items:center; gap:12px; cursor:pointer; border:0;
  background:#2a2a31; color:#fff; padding:10px 14px;
  font-family:var(--pixel); font-size:var(--fs-2xs); letter-spacing:.5px;
  box-shadow: inset 2px 2px 0 0 #4a4a52, inset -2px -2px 0 0 #0b0b0e;
}
.footip:hover{background:#34343c}
.footip:active{box-shadow: inset -2px -2px 0 0 #4a4a52, inset 2px 2px 0 0 #0b0b0e}
.footcopy{color:#9db197; font-family:var(--sans); font-size:var(--fs-note)}
.footlinks{display:flex; gap:6px 20px; flex-wrap:wrap}
.footlinks a{font-family:var(--sans); font-size:var(--fs-note); color:#cfcfcf; text-decoration:none}
.footlinks a:hover, .footlinks a[aria-current="page"]{color:#fff; text-decoration:underline; text-underline-offset:4px}

/* ---------------------------------------------------------------------------- LIVE STATUS
   Filled by status.js. Everything here starts hidden and is shown only once a real answer has
   arrived, so nothing on the page ever reads "0 online" or "-". */
[data-live][hidden]{display:none !important}
.livepill{
  display:inline-flex; align-items:center; gap:9px;
  font-family:var(--sans); font-size:var(--fs-note); color:#eaf2e6;
  background:#0f2a16; padding:7px 13px;
  box-shadow: inset 2px 2px 0 0 #1f5a2c, inset -2px -2px 0 0 #061309;
}
.livepill b{color:#7bf58c; font-weight:800}
.livedot{
  width:10px; height:10px; flex:0 0 10px; background:#4ade5f;
  box-shadow:0 0 0 3px rgba(74,222,95,.25);
  animation:livepulse 2s ease-in-out infinite;
}
@keyframes livepulse{
  0%,100%{box-shadow:0 0 0 3px rgba(74,222,95,.25)}
  50%    {box-shadow:0 0 0 7px rgba(74,222,95,.04)}
}
.livepill.big{font-size:var(--fs-body); padding:11px 18px}
.liveline{display:flex; flex-direction:column; align-items:center; gap:10px; margin:20px 0 0}
.livestat{font-family:var(--sans); font-size:var(--fs-note); color:#3a3a3a}
.livestat b{color:#14506b; font-weight:800}
@media (prefers-reduced-motion: reduce){ .livedot{animation:none} }

/* ---------------------------------------------------------------------------- HOME: THE CLIP
   A painting on the wall, not a wallpaper: the video is framed inside a panel, because the
   portal is already the moving background and two of them fight. */
.videoframe{
  max-width:920px; margin:0 auto; padding:10px; background:#6b4b2a;
  box-shadow: inset 4px 4px 0 0 #a07a4a, inset -4px -4px 0 0 #3a2812, 0 6px 0 0 rgba(0,0,0,.35);
}
.videoframe .clip{display:block; width:100%; aspect-ratio:16/9; background:#000; object-fit:cover; border:0}

/* ---------------------------------------------------------------------------- HOME: STEPS */
.steps{display:grid; grid-template-columns:repeat(auto-fit, minmax(250px, 1fr)); gap:12px}
.step{
  position:relative; display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:12px; padding:24px 16px 18px; background:#8B8B8B;
  box-shadow: inset 3px 3px 0 0 #373737, inset -3px -3px 0 0 #FFFFFF;
}
.stepno{
  position:absolute; left:10px; top:10px;
  font-family:var(--pixel); font-size:var(--fs-xs); color:#fff; background:var(--section);
  padding:6px 8px; text-shadow:1px 1px 0 rgba(0,0,0,.55); box-shadow:2px 2px 0 0 rgba(0,0,0,.3);
}
.steptext{display:flex; flex-direction:column; gap:6px}
.steptext b{font-family:var(--pixel); font-size:var(--fs-xs); color:#fff; text-shadow:2px 2px 0 rgba(0,0,0,.5)}
.steptext span{font-family:var(--sans); font-size:var(--fs-note); color:#eee; line-height:1.6}
.iphint{opacity:1; color:#3f3f3f}

/* ---------------------------------------------------------------------------- CAPTIONED SLOTS
   A slot that stands alone - not in a grid where its neighbours name it - carries its name under
   it. The hover still shows the recipe; the caption is what you read without hovering. */
.captioned{display:inline-flex; flex-direction:column; align-items:center; gap:8px; width:calc(var(--slot-cell) + 44px)}
.captioned .caption{
  font-family:var(--sans); font-size:var(--fs-note); font-weight:600; color:#2f2f2f;
  text-align:center; line-height:1.3;
}
.iconwall.named{gap:12px 16px; align-items:flex-start}

/* ---------------------------------------------------------------------------- WIKI NAV
   It began as in-page anchors: four chests on one long page and no way to jump between them. The
   wiki is five pages now, so the same strip links to them - Index, Machines, Items, Automation,
   Stews - and the current one carries aria-current. It is stamped from nav.py by stampnav.py,
   exactly like the top bar, because a nav copied into five pages is the failure that check exists
   for. The icons are each section's own first item. */
.subnav{
  position:sticky; top:var(--nav-height); z-index:50;
  display:flex; gap:6px; flex-wrap:wrap; justify-content:center;
  margin:0 0 20px; padding:8px;
  background:rgba(12,12,16,.78); box-shadow:0 3px 0 0 rgba(0,0,0,.4);
}
.subnav a{
  display:inline-flex; align-items:center; gap:8px; text-decoration:none;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#2b2b2b; background:#9c9c9c;
  padding:9px 12px; box-shadow: inset 2px 2px 0 0 #cfcfcf, inset -2px -2px 0 0 #666;
}
.subnav a img{width:18px; height:18px}
.subnav a:hover{background:#b6b6b6}
/* The page you are on, marked. Without it the strip says where you can go and not where you are,
   which on five near-identical pages is most of what a reader needs from it. */
.subnav a[aria-current="page"]{
  background:var(--panel); color:#141414;
  box-shadow: inset 2px 2px 0 0 #FFFFFF, inset -2px -2px 0 0 #555;
}
/* Anything an anchor can land on clears the two sticky bars above it. The tiles are here because
   /wiki/#machines - every anchor the one-page wiki published - now lands on a tile on the index. */
.chest[id], .hometile[id], .autosec[id]{scroll-margin-top:150px}
@media (max-width:900px){
  .subnav{position:static; padding:8px 0; gap:6px; background:none; box-shadow:none}
  .subnav a{font-family:var(--sans); font-size:13px; font-weight:600; padding:9px 10px; min-height:44px}
  .subnav a span{display:inline}
  .chest[id], .hometile[id], .autosec[id]{scroll-margin-top:140px}
}

/* ---------------------------------------------------------------------------- TIER TABLES, TWO UP
   Six families in one column left the panel two-thirds empty on a desktop. Two tables side by
   side from 900px, each with its own tier headings so a column still reads as one tier. */
.tiertables{display:grid; grid-template-columns:1fr; gap:14px 44px; justify-items:center}
@media (min-width:900px){ .tiertables{grid-template-columns:1fr 1fr} }
/* Rates were pixel type at its 11px floor. They are numbers to read, not glance at. */
.rate{font-family:var(--sans); font-size:var(--fs-note); font-weight:600; color:#2f2f2f; letter-spacing:0}
.rate b{color:#14506b; font-weight:700}

/* ---------------------------------------------------------------------------- RECIPE VIEWER
   Click any item and its recipe is drawn in the chest below the items, on the crafting table
   GUI, and stays there. The hover tooltip is still the fast path on a desktop; this is the one
   that works on a phone and the one you can look at while reading the rest of the panel. */
.recipeview{margin-top:24px; display:flex; flex-direction:column; align-items:center; gap:10px}
.recipeview .rvhint{font-family:var(--sans); font-size:var(--fs-note); color:#4a4a4a; margin:0; text-align:center}
.rvpanel{display:flex; flex-direction:column; align-items:center; gap:12px}
.rvpanel[hidden]{display:none}
.rvname{font-family:var(--pixel); font-size:var(--fs-sm); color:#3A3A3A; text-align:center; margin:0}
.rvcost{
  display:flex; flex-wrap:wrap; gap:8px 16px; justify-content:center; align-items:center;
  font-family:var(--sans); font-size:var(--fs-note); color:#2f2f2f;
}
.rvcost .rvlabel{color:#4a4a4a}
.rvcost .costrow{color:#2f2f2f; font-size:var(--fs-note)}
.rvcost .slot.chip{--size-n:calc(22 * var(--ui-scale))}
.rvcost .costrow i{font-weight:700; color:#14506b}
.rvmade{font-family:var(--sans); font-size:var(--fs-note); color:#4a4a4a}
.rvmade b{color:#14506b}
/* The selected item is held lighter, the game's own "this one" cue. */
.slot.rvsel{background-color:#dedede}
.slot[data-rv]{cursor:pointer}
/* Cropped to the crafting area alone: the player inventory rows say nothing about a recipe. */
.crafttable.compact{height:calc(82px * var(--gs))}
/* A WELL, NOT A CHEST INSIDE A CHEST. crafting_table.png carries a vanilla panel of its own -
   1px black, 2px white, then a #C6C6C6 face identical to ours - so laid on the chest it drew a
   second panel inside the first, which is what /wiki/machines/ looked like. Those three border
   pixels are cropped off here - four, because the corner pixel of the white bevel is on the
   diagonal and three left it behind as a lone white dot. The face is then continuous with the
   panel, the recipe viewer's own well is the frame, and the cells shift by the same four
   game-pixels so the measured coordinates still land on their slots. */
.rvpanel .crafttable{
  width:calc(169px * var(--gs));        /* x 4..172: the last column before the right border */
  background-position:calc(-4px * var(--gs)) calc(-4px * var(--gs));
}
.rvpanel .crafttable.compact{height:calc(78px * var(--gs))}
.rvpanel .crafttable > *{margin:calc(-4px * var(--gs)) 0 0 calc(-4px * var(--gs))}
@media (max-width:1000px){ .crafttable.compact{--gs:4} }
@media (max-width:760px){  .crafttable.compact{--gs:3} }
@media (max-width:420px){  .crafttable.compact{--gs:2} }


/* ---------------------------------------------------------------------------- 390px, ROUND TWO
   Three things pushed the DOCUMENT wider than the window at 390 - which is worse than any of them
   looks, because the whole page then scrolls sideways and every panel on it is off-centre.

   1. THE RECIPE GUI IS A FIXED WIDTH. .crafttable is 176 game-pixels times an INTEGER --gs, and it
      has to stay integer or the pixel art lands on half pixels and goes soft. At --gs:2 that is
      352px, and 390 minus the wrap's 44 and the chest's 40 leaves 306. There is no smaller integer
      worth having (--gs:1 draws 16px items), so the TABLE scrolls inside its own box instead and
      the page does not.
   2. .tiertable was width:max-content with max-width:100%, which does not shrink - it CLIPS, and
      what got clipped was the last column: the Giga IV ladder. fit-content is the one that means
      "as wide as you need, and no wider than there is".
   3. .stewtiles asked for a 320px minimum track inside a 306px column. min() lets the track give
      way on a narrow screen and keeps the two-up layout everywhere else.
   4. AND THE TOOLTIPS, which is the one that is invisible until you measure it. A .tip is
      position:absolute and visibility:hidden - hidden still LAYS OUT, and an absolutely positioned
      box contributes to its ancestors' scrollable overflow whether you can see it or not. At 390px
      a 330px tooltip centred on a slot near the right edge left the document 77px wider than the
      window. ui.js clamps a tooltip to the window when it is HOVERED, which is exactly too late
      for this.
      clip, not hidden: hidden would make the body a scroll container and take the sticky top bar
      with it. Naming only overflow-x leaves y visible, which clip permits and hidden does not. */
body{overflow-x:clip}
.rvpanel{width:100%; max-width:100%}
.rvtable{max-width:100%; overflow-x:auto}
.tiertable{width:fit-content; max-width:100%}
.stewtiles{grid-template-columns:repeat(auto-fit, minmax(min(320px, 100%), 1fr))}
.hometiles{grid-template-columns:repeat(auto-fit, minmax(min(260px, 100%), 1fr))}
.steps{grid-template-columns:repeat(auto-fit, minmax(min(250px, 100%), 1fr))}
.cardgrid{grid-template-columns:repeat(auto-fit, minmax(min(300px, 100%), 1fr))}

/* Rates at phone width: three per row, and the sans face at 16px did not fit three. */
@media (max-width:520px){ .rate{font-size:13px} }

/* ==========================================================================================
   AUTOMATION BLOCK PAGES — 2026-09-03

   /wiki/automation/ was five demos stacked on one page: a range dial, a furnace repair loop, a
   240-block tool explorer and three more, each with its readout somewhere under it. Kelp: "showing
   all examples on the single page is too much. Focus on improving the layout of these items and
   examples." So it is an index of six tiles and a page per block, and every one of those pages has
   the same two-part shape, generated by one function in build.py.

   THE READOUT IS THE EXPLANATION, so it sits BESIDE the thing it explains from 900px up rather
   than under it. Under it is where it was, and on a desktop a paragraph beneath a wide diagram is
   read after the reader has already given up guessing. Below 900 the two stack, demo first,
   because a 300px readout column is worse than no column.

   Everything here is NEW and appended: nothing above this heading is touched, so a restyle of
   .chest and its neighbours merges over the top of it cleanly.
   ========================================================================================== */

/* ------------------------------------------------------------------ the header row */
.backlink{
  display:inline-block; margin:0 0 12px;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#14506b; text-decoration:none;
}
.backlink:hover{text-decoration:underline}

.blockhead{display:grid; gap:24px; align-items:center; grid-template-columns:minmax(0,1fr)}
@media (min-width:900px){
  .blockhead{grid-template-columns:minmax(0,1fr) min-content}
}
.blockid{display:flex; flex-direction:column; align-items:flex-start; gap:2px; min-width:0}
/* Left, not centred: this is a masthead beside a recipe, not a gallery title. */
.chest .blockid .chesttitle{margin:10px 0 6px; text-align:left}
.blockline{
  font-family:var(--sans); font-size:var(--fs-body); color:#2f2f2f;
  margin:0; max-width:52ch;
}
/* The table is open and inline - a block page holds one recipe, so there is nothing to fetch and
   nothing to click between. .rvpanel already centres its own contents. */
.blockrecipe{min-width:0; max-width:100%}
.blockrecipe .rvpanel{gap:10px; max-width:100%}
/* Only once there IS a second column does the table shrink-wrap and sit against the right edge.
   Below that it is the full width of the panel - shrink-wrapping a 352px table inside a 306px
   column is what pushed it out through the chest's left bevel at 390px. */
@media (min-width:900px){
  .blockrecipe{justify-self:end}
  .blockrecipe .rvpanel{width:auto}
}
.blockrecipe .rvname{display:none}          /* the name is the chest title two inches to the left */
/* THE TABLE IS SMALLER HERE than in a section viewer, deliberately: it shares the row with the
   block's name and sentence rather than owning the width of the panel. --gs stays an INTEGER, or
   the GUI's pixel art lands on half pixels and goes soft, so it steps 3 -> 2 and not by fractions.
   These repeat the .crafttable.compact breakpoints because they come after them in the file. */
.blockrecipe .crafttable.compact{--gs:3}
@media (max-width:420px){ .blockrecipe .crafttable.compact{--gs:2} }
/* The roll-up chips under the table wrap instead of stretching the column. */
.blockrecipe .rvcost{flex-wrap:wrap; justify-content:center}

/* ------------------------------------------------------------------ a demo row */
.demorow{padding:26px 2px 2px}
.demorow + .demorow, .blockhead + .demorow{
  margin-top:16px; border-top:3px solid rgba(0,0,0,.16);
}
.demoname{
  font-family:var(--pixel); font-size:var(--fs-sm); color:var(--panel-ink);
  margin:0 0 14px; letter-spacing:.5px;
}
.demogrid{display:grid; gap:22px; grid-template-columns:1fr; align-items:start}
@media (min-width:900px){
  .demogrid{grid-template-columns:minmax(0,1.35fr) minmax(240px,.9fr)}
}
.demoleft{min-width:0}
/* The sunken well: the demo reads as something set INTO the panel, which is the same bevel the
   game gives an inventory, only inverted. */
.demowell{
  background:#8f8f8f; padding:12px;
  box-shadow:inset 4px 4px 0 0 #6e6e6e, inset -4px -4px 0 0 #b6b6b6;
  overflow:hidden;
}
.demowell .rsdemo{margin-top:0}
/* A vertical demo is one column of cells. Its well hugs that column instead of spanning
   the row and leaving most of itself empty; the controls sit centred under it. */
.demowell.narrow{width:max-content; max-width:100%; margin:0 auto}
.demowell.narrow + .rscontrols{justify-content:center}
.toolmore{font-family:var(--sans); font-size:var(--fs-note); color:#4a4a4a; margin:10px 0 0}
.demowell .furnwrap{display:flex; justify-content:center}
.demoleft .rscontrols{justify-content:flex-start}
/* The readout column reads as prose: left-aligned, and not fighting .chest's centring rules. */
.chest .demoside .rsread{margin:0; text-align:left}
.rsnote{
  font-family:var(--sans); font-size:var(--fs-note); color:#4a4a4a; margin:12px 0 0;
}
.rsnote b{color:#14506b}

/* Placer cells: the block it sets, and the item it will not. */
.rsplaced{outline:3px solid #4ade5f; outline-offset:-3px; background:#5f7a5f}
.rsplaced .badge{color:#d6ffd6}
.rsrefused{opacity:.5; outline:3px dashed #d06a6a; outline-offset:-3px}
[data-pitem][aria-pressed="true"]{
  background:#8fbcd4; box-shadow:inset 2px 2px 0 0 #d8eef8, inset -2px -2px 0 0 #4a7a92;
}

/* ------------------------------------------------------------------ the tool explorer */
.demorow.wide .toolout{max-width:none}
/* CLOSED, ALWAYS. 240 block icons is the single largest thing on any wiki page and it is a
   reference list, not the answer - the sentence above it is the answer. */
.toolblocks{margin:14px 0 0}
.toolblocks > summary{
  display:inline-block; cursor:pointer; list-style:none;
  font-family:var(--pixel); font-size:var(--fs-2xs); color:#2b2b2b; background:#AFAFAF;
  padding:10px 14px;
  box-shadow:inset 2px 2px 0 0 #FFFFFF, inset -2px -2px 0 0 #6a6a6a;
}
.toolblocks > summary::-webkit-details-marker{display:none}
.toolblocks > summary:hover{background:#BEBEBE}
.toolblocks[open] > summary{margin-bottom:14px}

/* ------------------------------------------------------------------ the second nav strip
   Same class, same rules as the wiki nav - it IS a .subnav - so this only stops the two strips
   from sitting on top of one another. */
.subnav.autonav{margin-top:6px}

/* A tile's text is a grid/flex child, and a flex child's min-width is auto - which is why a long
   sentence pushed out through the right-hand bevel instead of wrapping inside it. */
.hometext{min-width:0}
.hometext span{overflow-wrap:break-word}
/* A hard line break inside a wrapping .rscontrols: the placer's dust dial and its item picker are
   two controls, not one long run of buttons. */
.rsbreak{flex-basis:100%; height:0}
/* .chest centres its hints and readouts; inside a demo column they are left-aligned prose. */
.demoleft .rshint{margin-left:0; margin-right:0; width:100%}
/* The tool explorer is the one full-width row, so its sentence and its note are left-aligned prose
   rather than the centred chest notes .chest makes of them. */
.demorow.wide .toolout, .demorow.wide .toolnote{margin-left:0; margin-right:0; text-align:left}
.demorow.wide .toolpick{justify-content:flex-start}

/* Content groups use space for separation; the inventory slots keep their original bevels. */
.hometiles,.steps,.stewtiles{background:none; box-shadow:none; padding:0}
a:focus-visible,button:focus-visible,summary:focus-visible{outline:3px solid #51b9fb; outline-offset:4px}
@media(max-width:520px){.chest{padding:20px}}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

/* Secondary explanations stay available without crowding the main task. */
.moreinfo > summary{cursor:pointer; min-height:44px; padding:10px 0; font-size:var(--fs-note); font-weight:600}
.moreinfo[open] > summary{margin-bottom:12px}
.chest.moreinfo > summary{color:#293e30}
.rsbtn:disabled{opacity:.5; cursor:default}

/* MC.head's cached-skin crop (see ui.js). The element IS the face: the skin sheet is scaled to
   800% of it (64px sheet / 8px face), the face layer positioned at source (8,8) and the hat
   layer at (40,8) - a region starting at source pixel x sits at x/(64-8) of the scaled sheet,
   so 8/56 and 40/56. Two layers of the SAME image (set inline by MC.head), hat listed first so
   it draws on top. Doubled selector on purpose: the pages' own `.head` rules (worth.css,
   rankings.css) set `background:` shorthands and sizes at equal specificity, and this must win
   whatever order the stylesheets load in. Percentage sizing is what keeps the crop correct when
   those page rules resize the head at phone widths. */
.head.headskin{width:var(--headpx,24px); height:var(--headpx,24px);
  background-color:#1b2a20; background-repeat:no-repeat;
  background-size:800% 800%, 800% 800%;
  background-position:71.42857% 14.28571%, 14.28571% 14.28571%;
  image-rendering:pixelated}
