/* ══════════════════════════════════════════════════════════════════════════
   TILE GALLERY — /tiles.html

   Reuses the parlour palette from mahjong.css but drops the table's fixed,
   no-scroll layout: this page is a document, so it scrolls and reflows.
   ══════════════════════════════════════════════════════════════════════════ */

body.gallery {
  display: block;
  min-height: 100%;
  padding: 0 0 64px;
  background: var(--bg);
  background-image: radial-gradient(circle at 50% -10%, #2a2018 0%, #14100c 62%);
  color: var(--ink-dim);
  -webkit-font-smoothing: antialiased;   /* long-form reading, not pixel chrome */
}

.gwrap { width: 100%; max-width: 1080px; margin: 0 auto; padding: 0 20px; }

.ghead {
  position: sticky; top: 0; z-index: 20;
  background: rgba(20, 16, 12, .94);
  border-bottom: 2px solid var(--panel-hi);
  backdrop-filter: blur(6px);
  padding: 10px 0;
  margin-bottom: 26px;
}
.ghead .gwrap { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.ghead h1 {
  font-family: var(--font-display); font-size: 1.3rem; letter-spacing: .1em;
  color: var(--gold); font-weight: 700;
}
.ghead h1 small { color: var(--ink-dim); font-size: .72rem; letter-spacing: 0; margin-left: 10px; font-weight: 400; }

.btnlink {
  font-family: var(--font-display); font-size: .78rem; font-weight: 700;
  color: var(--ink); background: var(--panel-hi);
  border: 2px solid #55432e; border-bottom-width: 3px; border-radius: 3px;
  padding: 6px 12px; text-decoration: none; letter-spacing: .04em;
}
.btnlink:hover { background: #4b3a28; }

.intro { font-size: .9rem; line-height: 1.7; max-width: 68ch; margin-bottom: 18px; }

.jump { display: flex; gap: 10px; margin-bottom: 26px; flex-wrap: wrap; }
.jump a {
  font-size: .78rem; color: var(--gold); text-decoration: none;
  border: 2px solid #3a2d20; border-radius: 3px; padding: 5px 11px;
  font-family: var(--font-display); letter-spacing: .04em;
}
.jump a:hover { border-color: var(--gold); background: #1a140e; }

.ghead2 {
  font-family: var(--font-display); color: var(--gold);
  font-size: 1.15rem; letter-spacing: .07em;
  margin: 30px 0 4px; padding-bottom: 6px;
  border-bottom: 2px solid var(--panel-hi);
  scroll-margin-top: 70px;
}
.ghead2 .sub { color: var(--dim); font-size: .72rem; font-weight: 400; letter-spacing: 0; margin-left: 10px; }

.search {
  width: 100%; max-width: 420px; margin: 12px 0 6px;
  font-family: inherit; font-size: .85rem;
  background: #1a140e; color: var(--ink);
  border: 2px solid #4a3b2a; border-radius: 3px; padding: 8px 11px;
}
.search:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ── Tile grids ─────────────────────────────────────────────────────────── */

body.gallery section { margin: 26px 0 8px; }
body.gallery section h2 {
  font-family: var(--font-display); color: var(--ink);
  font-size: 1rem; letter-spacing: .06em; margin-bottom: 3px;
}
.lede { font-size: .82rem; line-height: 1.65; max-width: 70ch; margin-bottom: 12px; color: var(--dim); }

.grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
}
.cell {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  background: linear-gradient(180deg, #1f4a35 0%, #17392a 100%);
  border: 2px solid #3a2d20; border-radius: 4px;
  padding: 12px 6px 10px;
}
.cell canvas.tile {
  image-rendering: pixelated;
  display: block;
  /* ⚠️ No CSS drop-shadow. The tile now draws its own contact shadow on the
     felt inside the canvas, and the filter shadowed the CANVAS — including its
     transparent headroom — so a lifted tile trailed a dark bar above itself. */
  /* The canvas carries headroom above the tile so a hovered one can rise
     without being clipped by its own bitmap. It stays INSIDE the cell — a
     negative margin pulled it over the page background, which then showed
     through as a dark bar above any lifted tile. `tilepick.js` paints it the
     cell's own colour, so the extra height is invisible. */
}
.cell figcaption { text-align: center; line-height: 1.35; }
.cell figcaption b {
  display: block; color: var(--ink); font-size: .86rem;
  font-family: var(--font-display); letter-spacing: .03em;
}
.cell figcaption span { display: block; color: var(--dim); font-size: .68rem; }

/* ── Faan ladder ────────────────────────────────────────────────────────── */

.count { font-size: .74rem; color: var(--dim); margin: 2px 0 14px; }

.band { margin: 0 0 22px; }
.band h3 {
  display: flex; align-items: baseline; gap: 10px;
  margin: 22px 0 8px; padding-bottom: 5px;
  border-bottom: 1px solid #3a2d20;
}
.band h3 .faan {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 700;
  color: var(--gold); letter-spacing: .05em;
}
.band h3 .faanN { font-size: .72rem; color: var(--dim); }

.faanrow {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
  background: var(--panel); border: 2px solid #3a2d20; border-radius: 4px;
  padding: 11px 14px; margin-bottom: 7px;
}
.faanrow .meta { flex: 1 1 260px; min-width: 0; }
.faanrow .meta b {
  font-family: var(--font-display); color: var(--gold);
  font-size: .98rem; letter-spacing: .05em; margin-right: 8px;
}
.faanrow .meta .en { color: var(--ink); font-size: .82rem; }
.faanrow .meta p { font-size: .8rem; line-height: 1.6; color: var(--ink-dim); margin-top: 4px; }
/* What the tiles beside it are actually showing. Set apart from the rule
   itself, because for the situational faan the picture is only half the
   answer — the other half is where the winning tile came from. */
.faanrow .meta .exnote {
  font-size: .75rem; color: var(--dim);
  border-left: 2px solid #3a2d20; padding-left: 8px; margin-top: 6px;
}
.faanrow .meta .tag {
  display: inline-block; margin-top: 6px;
  font-size: .66rem; letter-spacing: .06em; text-transform: uppercase;
  color: #e8c98a; background: #3a2d20; border-radius: 2px; padding: 2px 7px;
}
.faanrow .tiles {
  display: flex; gap: 1px; flex-wrap: wrap; align-items: flex-end;
  flex: 0 1 auto; max-width: 100%;
}
.faanrow .tiles canvas {
  image-rendering: pixelated; display: block;
  border-radius: 1px;
}

@media (max-width: 620px) {
  .gwrap { padding: 0 12px; }
  .grid { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); }
  .faanrow { gap: 10px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   HAND SIMULATOR
   Two columns on a wide screen — build on the left, verdict on the right, so
   the score updates in place as you click rather than scrolling away.
   ══════════════════════════════════════════════════════════════════════════ */

.simwrap {
  display: grid; gap: 16px; margin: 14px 0 10px;
  grid-template-columns: minmax(0, 1.25fr) minmax(260px, 1fr);
  align-items: start;
}
@media (max-width: 900px) { .simwrap { grid-template-columns: 1fr; } }

.simbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 6px;
}
.simcount {
  font-family: ui-monospace, Consolas, monospace; font-size: .76rem; color: var(--dim);
}
.ghostbtn {
  font-family: var(--font-display); font-size: .74rem; font-weight: 700;
  color: var(--ink-dim); background: transparent;
  border: 2px solid #4a3b2a; border-radius: 3px; padding: 4px 11px; cursor: pointer;
  letter-spacing: .04em;
}
.ghostbtn:hover { background: #4b3a28; color: var(--ink); }

/* The hand under construction */
.simhand {
  display: flex; flex-wrap: wrap; gap: 2px; align-items: flex-end;
  min-height: 62px; padding: 8px;
  background: linear-gradient(180deg, #1f4a35 0%, #17392a 100%);
  border: 2px solid #3a2d20; border-radius: 4px;
}
.simhand .empty { color: #8fb9a2; font-size: .8rem; margin: 12px auto; font-style: italic; }
.simhand .divider {
  width: 2px; align-self: stretch; margin: 2px 6px;
  background: repeating-linear-gradient(180deg, #d8a544 0 3px, transparent 3px 6px);
}
.held {
  padding: 0; border: none; background: none; cursor: pointer; line-height: 0;
  border-radius: 2px;
}
.held canvas { image-rendering: pixelated; display: block; }
.held:hover { transform: translateY(-4px); }
.held:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.simopts {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: center;
  margin: 10px 0 4px; font-size: .78rem; color: var(--ink-dim);
}
.simopts label { display: flex; align-items: center; gap: 6px; }
.simopts select {
  font-family: inherit; font-size: .76rem; background: #1a140e; color: var(--ink);
  border: 2px solid #4a3b2a; border-radius: 3px; padding: 3px 6px;
}

.simex { margin: 10px 0 6px; }
.exlist { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 5px; }
.example {
  font-family: var(--font-display); font-size: .72rem; letter-spacing: .03em;
  color: var(--gold); background: #1a140e;
  border: 1px solid #4a3b2a; border-radius: 2px; padding: 4px 9px; cursor: pointer;
}
.example:hover { border-color: var(--gold); background: #241c11; }

/* The picker */
.simpicker { margin-top: 12px; }
.pickrow { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; flex-wrap: wrap; }
.picklabel {
  flex: 0 0 96px; font-family: var(--font-display); font-size: .74rem;
  color: var(--dim); letter-spacing: .04em;
}
.pickstrip { display: flex; gap: 3px; flex-wrap: wrap; }
.pick {
  position: relative; padding: 0; line-height: 0; cursor: pointer;
  background: none; border: 2px solid transparent; border-radius: 3px;
}
.pick canvas {
  image-rendering: pixelated; display: block;
  width: 24px; height: 36px;
}
.pick:hover { border-color: var(--gold); }
.pick:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.pick.used { border-color: #6b5735; }
.pick.full { opacity: .42; cursor: not-allowed; }
.pick .left {
  position: absolute; right: -2px; bottom: -3px;
  min-width: 13px; height: 13px; padding: 0 2px;
  background: var(--gold); color: #241c14; border-radius: 2px;
  font-family: ui-monospace, Consolas, monospace; font-size: .6rem; font-weight: 700;
  line-height: 13px; text-align: center;
}

.simflash {
  min-height: 1.2em; margin-top: 8px; font-size: .76rem; color: #e8b06a;
  opacity: 0; transition: opacity .15s;
}
.simflash.show { opacity: 1; }

/* The verdict */
.simright { position: sticky; top: 74px; }
.simresult { display: flex; flex-direction: column; gap: 10px; }

.verdict {
  background: #1a140e; border: 2px solid #55432e; border-left: 4px solid #55432e;
  border-radius: 3px; padding: 12px 14px;
}
.verdict.good    { border-left-color: var(--green); }
.verdict.short   { border-left-color: var(--red); }
.verdict.partial { border-left-color: var(--gold); }
.verdict.nowin   { border-left-color: var(--red); }
.verdict.empty   { border-left-color: #4a3b2a; }
.verdict > b {
  display: block; font-family: var(--font-display); color: var(--gold);
  font-size: .96rem; letter-spacing: .04em; margin-bottom: 5px;
}
.verdict p { font-size: .8rem; line-height: 1.6; color: var(--ink-dim); margin: 0; }
.verdict .total { display: flex; align-items: baseline; gap: 6px; margin-bottom: 6px; }
.verdict .total .num {
  font-family: ui-monospace, Consolas, monospace; font-size: 2.1rem;
  font-weight: 700; color: var(--gold); line-height: 1;
}
.verdict .total .unit { font-family: var(--font-display); color: var(--gold); font-size: 1rem; }
.verdict .total .capped {
  margin-left: auto; font-size: .66rem; letter-spacing: .06em; text-transform: uppercase;
  color: #e8c98a; background: #3a2d20; border-radius: 2px; padding: 2px 7px;
}
.verdict .headline { color: var(--ink); font-weight: 700; }
.verdict.good .headline { color: #7fc79a; }
.verdict.short .headline { color: #e8867a; }
.verdict .pay { margin-top: 5px; }
.verdict .waits { display: flex; gap: 3px; flex-wrap: wrap; margin-top: 8px; }
.verdict .waits canvas { image-rendering: pixelated; width: 22px; height: 33px; }

table.breakdown { width: 100%; border-collapse: collapse; font-size: .78rem; }
table.breakdown td { padding: 5px 7px; border-bottom: 1px solid #33281c; }
table.breakdown td.k {
  font-family: var(--font-display); color: var(--gold);
  white-space: nowrap; font-weight: 700; width: 1%;
}
table.breakdown td.v {
  text-align: right; width: 1%; color: var(--gold); font-weight: 700;
  font-family: ui-monospace, Consolas, monospace;
}
table.breakdown tr.sum td {
  border-top: 2px solid #55432e; border-bottom: none;
  color: var(--ink); padding-top: 7px;
}
table.breakdown tr.sum td.k, table.breakdown tr.sum td.v { color: var(--ink); }

details.alts {
  background: #1a140e; border: 1px solid #3a2d20; border-radius: 3px; padding: 0 11px;
}
details.alts summary {
  cursor: pointer; padding: 7px 0; font-size: .74rem; color: var(--dim);
}
details.alts p { font-size: .74rem; color: var(--ink-dim); padding-bottom: 7px; margin: 0; }

/* ── Tile → table preview ────────────────────────────────────────────────
   The tile in a gallery cell is a button now. It must still look like a tile
   sitting on the cell, not like a form control, so the button is stripped to
   nothing and only the affordances (cursor, lift, focus ring) are added. */
.tilebtn {
  background: none; border: 0; padding: 0; margin: 0;
  cursor: zoom-in; display: block; line-height: 0;
  transition: transform .12s ease;
}
/* ⚠️ No CSS nudge any more — the tile is picked up for real, in 3D, by
   `tilepick.js`. Keeping both gave a lift on top of a lift. */
.tilebtn:hover { transform: none; }
.tilebtn:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

.tpv {
  position: fixed; inset: 0; z-index: 50;
  display: none; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(6, 4, 3, .78);
}
.tpv.show { display: flex; }
body.tpvopen { overflow: hidden; }

.tpvbox {
  background: linear-gradient(180deg, #241c11 0%, #1a140e 100%);
  border: 2px solid #3a2d20; border-radius: 6px;
  padding: 14px 16px 16px;
  max-width: min(640px, 100%);
  max-height: 100%; overflow-y: auto;
  box-shadow: 0 18px 50px rgba(0, 0, 0, .6);
}
.tpvhead { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.tpvhead h3 {
  color: var(--gold); font-family: var(--font-display);
  font-size: 1.05rem; letter-spacing: .04em; margin: 0 0 10px;
}
.tpvclose {
  background: #3a2d20; color: var(--ink); border: 0; border-radius: 3px;
  cursor: pointer; font-size: .8rem; line-height: 1;
  padding: 7px 9px; margin-bottom: 10px;
}
.tpvclose:hover { background: #4b3a28; }
.tpvclose:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* The canvas is pixel art at an integer scale: never let the browser smooth
   it, and never let it grow to a fractional multiple. */
.tpvcv {
  image-rendering: pixelated;
  display: block; width: 100%; height: auto;
  border: 1px solid #0f3524; border-radius: 3px;
}
.tpvsub {
  font-size: .74rem; line-height: 1.55; color: var(--dim);
  margin: 10px 0 0;
  /* Was `max-width: 60ch`, which is a fine measure for a page of prose
     and wrong here: the box is already capped at 640px, so the cap
     applied twice and the claim descriptions broke a third short of the
     panel, leaving a ragged column with empty space beside it. */
}
/* The four things a tile can do. A segmented row rather than a dropdown: the
   disabled states are the teaching — an honour with 上 greyed out says more
   than a menu that quietly omits it. */
.tpvmodes {
  display: flex; flex-wrap: wrap; gap: 4px;
  justify-content: center; margin: 8px 0 2px;
}
.tpvmodes button {
  font: inherit; font-size: .74rem; cursor: pointer;
  padding: 4px 10px; border-radius: 3px;
  background: #1a140e; color: var(--ink-dim);
  border: 1px solid #4a3b2a;
}
.tpvmodes button:hover:not(:disabled) { border-color: var(--gold); color: var(--ink); }
.tpvmodes button.on {
  background: #2a2013; border-color: var(--gold); color: var(--gold); font-weight: 700;
}
.tpvmodes button:disabled { opacity: .35; cursor: not-allowed; }

.tpvbar { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

@media (prefers-reduced-motion: reduce) {
  .tilebtn:hover { transform: none; }
}

/* ── The 3D experiment ─────────────────────────────────────────────────────
   Styling for `tilelab.js`. Deliberately plain: the panels are the content
   and anything decorative here would be competing with the thing being
   judged. Placed BEFORE the coarse-pointer block below so that block stays
   last, which is the convention this sheet and mahjong.css both rely on. */
.labbar {
  display: flex; flex-wrap: wrap; gap: 14px 20px; align-items: center;
  margin: 14px 0 18px; padding: 12px 14px;
  background: rgba(0, 0, 0, .22); border: 1px solid rgba(216, 180, 90, .22);
  border-radius: 6px;
}
.labctl { display: flex; align-items: center; gap: 8px; font-size: .84rem; }
.labctl > span { color: var(--dim, #a9a496); white-space: nowrap; }
.labctl input[type="range"] { width: 128px; }
.labctl output {
  min-width: 46px; font-variant-numeric: tabular-nums;
  color: var(--accent, #d8b45a);
}

/* One column, always. These panels are meant to be compared by looking
   straight down the page — side by side at 576px each they would either
   scroll horizontally or shrink below the size the tiles are drawn at, and a
   downscaled pixel tile is exactly the artefact this page exists to check
   for. */
.labgrid { display: flex; flex-direction: column; gap: 10px; }
.labpanel { display: flex; flex-direction: column; gap: 4px; }
.lablabel {
  font-size: .78rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--dim, #a9a496);
}
/* `image-rendering: pixelated` is the whole point — a browser that smooths
   this canvas on upscale would hide the softening the experiment is looking
   for, and report a false pass.

   ⚠️ THE CANVAS STAYS 576 LOGICAL PIXELS WIDE. `art.js` switches to its table
   lighting at exactly that width, so a panel drawn at any other backing size
   would be comparing the projection against the WRONG renderer — which is the
   one thing this page must not do. Screen-wide is a CSS scale of a 576px
   canvas, and because the scale is nearest-neighbour it is a magnifying glass
   rather than a resample: every artefact gets bigger along with the tile.

   ⚠️ THE WIDTH IS SET IN JS — see `fitPanels` in tilelab.js. `width: 100%`
   here would give a 1920px window a fractional scale, and nearest-neighbour at
   a fractional scale gives some source pixels three device pixels and others
   four. On this page that is unforgivable: it would put ragged edges on the
   tiles that ARE NOT IN THE TILES, on the one page whose whole job is telling
   a rendering artefact from a real one. `fitPanels` reaches the page edge the
   other way round — it sizes the CANVAS so that a whole-number scale lands on
   the page width. The rule below is only the pre-script fallback. */
.labcanvas {
  display: block; width: 576px; max-width: 100%; height: auto;
  image-rendering: pixelated;
  border: 1px solid rgba(0, 0, 0, .5); border-radius: 3px;
}

/* Break the specimen out of `.gwrap`'s 1080px column and run it to the edges
   of the window.

   ⚠️ `100vw` INCLUDES THE SCROLLBAR, so this deliberately overhangs by about
   half a scrollbar on each side, and `overflow-x: clip` on the body swallows
   it. The overhang lands in this element's own 12px padding, so nothing is
   ever cut off a canvas.

   ⚠️ `clip`, NOT `hidden`. `overflow: hidden` makes the body a scroll
   container, which kills `position: sticky` on `.ghead`. `clip` does not.
   (Measured 6 Aug 2026: `.ghead` is not actually sticking on this page today,
   for a reason that predates this rule and is nothing to do with it — with
   these three lines deleted it still unsticks at the same scroll position.
   The choice stands anyway, because the day that bug is fixed `hidden` would
   silently undo the fix.)

   ⚠️ It looks unnecessary under a headless browser. Chromium headless uses
   OVERLAY scrollbars, so `100vw` equals the client width and nothing
   overhangs — the overhang this clips only exists on a real desktop browser
   with a classic reserved-gutter scrollbar. Do not delete it on the strength
   of a green screenshot test. */
body.gallery { overflow-x: clip; }
#tilelab {
  margin-left: calc((100% - 100vw) / 2);
  margin-right: calc((100% - 100vw) / 2);
  padding: 0 12px;
}
/* CENTRED, and only as wide as a hand. `fitPanels` sizes the canvas to one
   full mahjong hand and then magnifies it by the largest whole number that
   fits, so there is always a remainder — splitting it evenly is what puts the
   rack in the middle of the page instead of hard against the left edge. */
#tilelab .labgrid { align-items: center; }
#tilelab .labpanel { align-items: center; }
#tilelab .lablabel { align-self: center; }
/* Prose does not want to be 1900px wide. Only the canvases go full width. */
#tilelab > p, #tilelab .labbar, #tilelab .labhand {
  max-width: 1040px; margin-left: auto; margin-right: auto;
}

/* ── Choosing the hand ────────────────────────────────────────────────────
   The picker only ADDS and the strip only REMOVES — see the note in
   tilelab.js for why one control doing both is a worse deal than it looks. */
.labhand { margin: 0 auto 16px; }
.labhandbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.labhandcount {
  font-size: .82rem; color: var(--accent, #d8b45a);
  font-variant-numeric: tabular-nums; margin-right: auto;
}
.labstrip {
  display: flex; flex-wrap: wrap; gap: 4px; align-items: flex-end;
  margin-top: 10px; min-height: 40px;
}
.labempty { font-size: .82rem; color: var(--dim, #a9a496); align-self: center; }
.labstriptile, .labpick {
  position: relative; padding: 2px; line-height: 0; cursor: pointer;
  background: rgba(0, 0, 0, .3);
  border: 1px solid rgba(216, 180, 90, .2); border-radius: 3px;
}
.labstriptile canvas, .labpick canvas {
  display: block; width: 33px; height: auto; image-rendering: pixelated;
}
.labstriptile:hover, .labpick:hover:not(:disabled) {
  border-color: var(--accent, #d8b45a); background: rgba(216, 180, 90, .12);
}
.labpicker {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 10px; padding: 10px;
  background: rgba(0, 0, 0, .22); border: 1px solid rgba(216, 180, 90, .22);
  border-radius: 6px;
}
/* A gap where the suit changes, so the row reads as the tile set. */
.labpick.brk { margin-left: 14px; }
.labpick.has { border-color: rgba(216, 180, 90, .55); }
.labpick:disabled { opacity: .3; cursor: not-allowed; }
.labpickn {
  position: absolute; right: -3px; bottom: -3px;
  min-width: 15px; height: 15px; padding: 0 3px;
  font-size: .66rem; line-height: 15px; text-align: center;
  font-family: var(--font-ui); color: #14100c;
  background: var(--accent, #d8b45a); border-radius: 8px;
}
.labpickn:empty { display: none; }

/* ── Finger-sized controls ──────────────────────────────────────────────
   The counterpart of the `pointer: coarse` block in mahjong.css, for the
   controls this page adds on top of it. Same reasoning, same rule: floors
   only, so a control that already cleared 44px is left exactly where it was.

   `.jump` is the one that matters most. Three section links sitting 10px
   apart at 29px tall is the classic mis-tap row — the targets are small AND
   adjacent, so the cost of missing is landing on the wrong one rather than
   on nothing, which is the more annoying of the two failures. */
@media (pointer: coarse) {
  .btnlink, .jump a, .ghostbtn {
    min-height: 44px; display: inline-flex; align-items: center;
  }
  .search { min-height: 44px; }
  /* The tile buttons in the gallery are the page's real content, and they are
     already comfortably over the floor — left alone on purpose, so this block
     cannot re-flow the sheet it is meant to protect.

     The LAB's chooser tiles are the exception: at 33px wide they are under the
     floor, and 42 of them sit 4px apart, which is the mis-tap row this block
     exists for. A floor on the width only — the height already clears it, and
     forcing that too would stretch the frame away from the tile inside it. */
  .labpick, .labstriptile { min-width: 44px; justify-content: center; display: inline-flex; }
}
